fix: updated task list design in task tab
This commit is contained in:
parent
c20d296b35
commit
dc20b4c36e
@ -27,7 +27,7 @@
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="activities?.length" class="flex-1 overflow-y-auto">
|
<div v-if="activities?.length" class="flex-1 overflow-y-auto">
|
||||||
<div v-if="title == 'Notes'" class="grid grid-cols-3 gap-4 px-10 py-5 pt-0">
|
<div v-if="title == 'Notes'" class="grid grid-cols-3 gap-4 px-10 pb-5">
|
||||||
<div
|
<div
|
||||||
v-for="note in activities"
|
v-for="note in activities"
|
||||||
class="group flex h-48 cursor-pointer flex-col justify-between gap-2 rounded-md bg-gray-50 px-4 py-3 hover:bg-gray-100"
|
class="group flex h-48 cursor-pointer flex-col justify-between gap-2 rounded-md bg-gray-50 px-4 py-3 hover:bg-gray-100"
|
||||||
@ -77,26 +77,64 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="title == 'Tasks'">
|
<div v-else-if="title == 'Tasks'" class="px-10 pb-5">
|
||||||
<div v-for="(task, i) in activities">
|
<div v-for="(task, i) in activities">
|
||||||
<div class="grid grid-cols-[30px_minmax(auto,_1fr)] gap-4 px-10">
|
<div
|
||||||
<div
|
class="flex cursor-pointer gap-6 rounded p-2.5 duration-300 ease-in-out hover:bg-gray-50"
|
||||||
class="relative flex justify-center after:absolute after:left-[50%] after:top-0 after:-z-10 after:border-l after:border-gray-200"
|
@click="showTask(task)"
|
||||||
:class="i != activities.length - 1 ? 'after:h-full' : 'after:h-4'"
|
>
|
||||||
>
|
<div class="flex flex-1 flex-col gap-1.5 text-base">
|
||||||
<div
|
<div class="font-medium text-gray-900">
|
||||||
class="z-10 mt-[15px] flex h-7 w-7 items-center justify-center rounded-full bg-gray-100"
|
{{ task.title }}
|
||||||
>
|
</div>
|
||||||
<TaskIcon class="text-gray-800" />
|
<div class="flex gap-1.5 text-gray-800">
|
||||||
|
<div class="flex items-center gap-1.5">
|
||||||
|
<UserAvatar :user="task.assigned_to" size="xs" />
|
||||||
|
{{ getUser(task.assigned_to).full_name }}
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center justify-center">
|
||||||
|
<DotIcon class="h-2.5 w-2.5 text-gray-600" :radius="2" />
|
||||||
|
</div>
|
||||||
|
<div class="flex gap-2">
|
||||||
|
<CalendarIcon />
|
||||||
|
<Tooltip :text="dateFormat(task.due_date, 'ddd, MMM D, YYYY')">
|
||||||
|
{{ dateFormat(task.due_date, 'D MMM') }}
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center justify-center">
|
||||||
|
<DotIcon class="h-2.5 w-2.5 text-gray-600" :radius="2" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Badge
|
||||||
|
variant="solid"
|
||||||
|
:class="
|
||||||
|
task.priority == 'High'
|
||||||
|
? '!bg-red-200 text-red-800'
|
||||||
|
: task.priority == 'Medium'
|
||||||
|
? '!bg-yellow-200 text-yellow-700'
|
||||||
|
: '!bg-gray-200 !text-gray-600'
|
||||||
|
"
|
||||||
|
size="sm"
|
||||||
|
:label="task.priority"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div class="flex items-center">
|
||||||
class="mb-3 flex max-w-[70%] flex-col gap-3 rounded-md bg-gray-50 p-4"
|
<Dropdown
|
||||||
@click="showTask(task)"
|
:options="taskStatusOptions(updateTaskStatus, task)"
|
||||||
>
|
@click.stop
|
||||||
{{ task.title }}
|
>
|
||||||
|
<Button variant="ghosted" class="hover:bg-gray-300">
|
||||||
|
<TaskStatusIcon :status="task.status" />
|
||||||
|
</Button>
|
||||||
|
</Dropdown>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="i < activities.length - 1"
|
||||||
|
class="mx-2 h-px border-t border-gray-200"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="title == 'Calls'">
|
<div v-else-if="title == 'Calls'">
|
||||||
@ -543,6 +581,8 @@ import PhoneIcon from '@/components/Icons/PhoneIcon.vue'
|
|||||||
import NoteIcon from '@/components/Icons/NoteIcon.vue'
|
import NoteIcon from '@/components/Icons/NoteIcon.vue'
|
||||||
import TaskIcon from '@/components/Icons/TaskIcon.vue'
|
import TaskIcon from '@/components/Icons/TaskIcon.vue'
|
||||||
import DurationIcon from '@/components/Icons/DurationIcon.vue'
|
import DurationIcon from '@/components/Icons/DurationIcon.vue'
|
||||||
|
import CalendarIcon from '@/components/Icons/CalendarIcon.vue'
|
||||||
|
import TaskStatusIcon from '@/components/Icons/TaskStatusIcon.vue'
|
||||||
import PlayIcon from '@/components/Icons/PlayIcon.vue'
|
import PlayIcon from '@/components/Icons/PlayIcon.vue'
|
||||||
import LeadsIcon from '@/components/Icons/LeadsIcon.vue'
|
import LeadsIcon from '@/components/Icons/LeadsIcon.vue'
|
||||||
import DealsIcon from '@/components/Icons/DealsIcon.vue'
|
import DealsIcon from '@/components/Icons/DealsIcon.vue'
|
||||||
@ -559,6 +599,7 @@ import {
|
|||||||
dateTooltipFormat,
|
dateTooltipFormat,
|
||||||
secondsToDuration,
|
secondsToDuration,
|
||||||
startCase,
|
startCase,
|
||||||
|
taskStatusOptions,
|
||||||
} from '@/utils'
|
} from '@/utils'
|
||||||
import { usersStore } from '@/stores/users'
|
import { usersStore } from '@/stores/users'
|
||||||
import { contactsStore } from '@/stores/contacts'
|
import { contactsStore } from '@/stores/contacts'
|
||||||
@ -572,6 +613,7 @@ import {
|
|||||||
createResource,
|
createResource,
|
||||||
createListResource,
|
createListResource,
|
||||||
call,
|
call,
|
||||||
|
Badge,
|
||||||
} from 'frappe-ui'
|
} from 'frappe-ui'
|
||||||
import { ref, computed, h, defineModel, markRaw, watch } from 'vue'
|
import { ref, computed, h, defineModel, markRaw, watch } from 'vue'
|
||||||
|
|
||||||
@ -837,6 +879,17 @@ function showTask(t) {
|
|||||||
showTaskModal.value = true
|
showTaskModal.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateTaskStatus(status, task) {
|
||||||
|
call('frappe.client.set_value', {
|
||||||
|
doctype: 'CRM Task',
|
||||||
|
name: task.name,
|
||||||
|
fieldname: 'status',
|
||||||
|
value: status,
|
||||||
|
}).then(() => {
|
||||||
|
tasks.reload()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
watch([reload, reload_email], ([reload_value, reload_email_value]) => {
|
watch([reload, reload_email], ([reload_value, reload_email_value]) => {
|
||||||
if (reload_value || reload_email_value) {
|
if (reload_value || reload_email_value) {
|
||||||
versions.reload()
|
versions.reload()
|
||||||
|
|||||||
16
frontend/src/components/Icons/CalendarIcon.vue
Normal file
16
frontend/src/components/Icons/CalendarIcon.vue
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<template>
|
||||||
|
<svg
|
||||||
|
width="16"
|
||||||
|
height="16"
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M4.5 1C4.77614 1 5 1.22386 5 1.5V2.5L5 2.50056C5.15613 2.5 5.32244 2.5 5.5 2.5H10.5C10.6776 2.5 10.8439 2.5 11 2.50056C11 2.50037 11 2.50019 11 2.5V1.5C11 1.22386 11.2239 1 11.5 1C11.7761 1 12 1.22386 12 1.5V2.5C12 2.50803 11.9998 2.51602 11.9994 2.52395C12.49 2.55387 12.838 2.62116 13.135 2.77248C13.6054 3.01217 13.9878 3.39462 14.2275 3.86502C14.5 4.3998 14.5 5.09987 14.5 6.5V10.5C14.5 11.9001 14.5 12.6002 14.2275 13.135C13.9878 13.6054 13.6054 13.9878 13.135 14.2275C12.6002 14.5 11.9001 14.5 10.5 14.5H5.5C4.09987 14.5 3.3998 14.5 2.86502 14.2275C2.39462 13.9878 2.01217 13.6054 1.77248 13.135C1.5 12.6002 1.5 11.9001 1.5 10.5V6.5C1.5 5.09987 1.5 4.3998 1.77248 3.86502C2.01217 3.39462 2.39462 3.01217 2.86502 2.77248C3.16202 2.62116 3.51 2.55387 4.00056 2.52395C4.00019 2.51602 4 2.50803 4 2.5V1.5C4 1.22386 4.22386 1 4.5 1ZM5.5 3.5H10.5C11.2166 3.5 11.6938 3.50078 12.0606 3.53074C12.4156 3.55975 12.5781 3.61105 12.681 3.66349C12.9632 3.8073 13.1927 4.03677 13.3365 4.31901C13.389 4.42194 13.4403 4.5844 13.4693 4.93944C13.4916 5.21337 13.4977 5.54901 13.4994 6H2.50061C2.50226 5.54901 2.50836 5.21337 2.53074 4.93944C2.55975 4.5844 2.61105 4.42194 2.66349 4.31901C2.8073 4.03677 3.03677 3.8073 3.31901 3.66349C3.42194 3.61105 3.5844 3.55975 3.93944 3.53074C4.30615 3.50078 4.78343 3.5 5.5 3.5ZM2.5 7V10.5C2.5 11.2166 2.50078 11.6939 2.53074 12.0606C2.55975 12.4156 2.61105 12.5781 2.66349 12.681C2.8073 12.9632 3.03677 13.1927 3.31901 13.3365C3.42194 13.389 3.5844 13.4403 3.93944 13.4693C4.30615 13.4992 4.78343 13.5 5.5 13.5H10.5C11.2166 13.5 11.6938 13.4992 12.0606 13.4693C12.4156 13.4403 12.5781 13.389 12.681 13.3365C12.9632 13.1927 13.1927 12.9632 13.3365 12.681C13.389 12.5781 13.4403 12.4156 13.4693 12.0606C13.4992 11.6939 13.5 11.2166 13.5 10.5V7H2.5Z"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
@ -6,6 +6,14 @@
|
|||||||
fill="none"
|
fill="none"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
>
|
>
|
||||||
<circle cx="8" cy="8" r="3.5" fill="currentColor" />
|
<circle cx="8" cy="8" :r="radius" fill="currentColor" />
|
||||||
</svg>
|
</svg>
|
||||||
</template>
|
</template>
|
||||||
|
<script setup>
|
||||||
|
const props = defineProps({
|
||||||
|
radius: {
|
||||||
|
type: Number,
|
||||||
|
default: 3.5,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|||||||
@ -37,7 +37,7 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<Dropdown :options="statusOptions()">
|
<Dropdown :options="taskStatusOptions(updateTaskStatus)">
|
||||||
<Button :label="_task.status" class="w-full justify-between">
|
<Button :label="_task.status" class="w-full justify-between">
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
<TaskStatusIcon :status="_task.status" />
|
<TaskStatusIcon :status="_task.status" />
|
||||||
@ -64,7 +64,7 @@
|
|||||||
input-class="border-none"
|
input-class="border-none"
|
||||||
:formatValue="(val) => val.split('-').reverse().join('-')"
|
:formatValue="(val) => val.split('-').reverse().join('-')"
|
||||||
/>
|
/>
|
||||||
<Dropdown :options="priorityOptions()">
|
<Dropdown :options="taskPriorityOptions(updateTaskPriority)">
|
||||||
<Button :label="_task.priority" class="w-full justify-between">
|
<Button :label="_task.priority" class="w-full justify-between">
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
<TaskPriorityIcon :priority="_task.priority" />
|
<TaskPriorityIcon :priority="_task.priority" />
|
||||||
@ -81,7 +81,7 @@
|
|||||||
import TaskStatusIcon from '@/components/Icons/TaskStatusIcon.vue'
|
import TaskStatusIcon from '@/components/Icons/TaskStatusIcon.vue'
|
||||||
import TaskPriorityIcon from '@/components/Icons/TaskPriorityIcon.vue'
|
import TaskPriorityIcon from '@/components/Icons/TaskPriorityIcon.vue'
|
||||||
import UserAvatar from '@/components/UserAvatar.vue'
|
import UserAvatar from '@/components/UserAvatar.vue'
|
||||||
import { activeAgents } from '@/utils'
|
import { activeAgents, taskStatusOptions, taskPriorityOptions } from '@/utils'
|
||||||
import { usersStore } from '@/stores/users'
|
import { usersStore } from '@/stores/users'
|
||||||
import {
|
import {
|
||||||
TextInput,
|
TextInput,
|
||||||
@ -112,34 +112,23 @@ const emit = defineEmits(['updateTask'])
|
|||||||
|
|
||||||
const title = ref(null)
|
const title = ref(null)
|
||||||
const editMode = ref(false)
|
const editMode = ref(false)
|
||||||
const _task = ref({})
|
const _task = ref({
|
||||||
|
title: '',
|
||||||
|
description: '',
|
||||||
|
assigned_to: '',
|
||||||
|
due_date: '',
|
||||||
|
status: 'Backlog',
|
||||||
|
priority: 'Low',
|
||||||
|
})
|
||||||
|
|
||||||
const { getUser } = usersStore()
|
const { getUser } = usersStore()
|
||||||
|
|
||||||
function statusOptions() {
|
function updateTaskStatus(status) {
|
||||||
return ['Backlog', 'Todo', 'In Progress', 'Done', 'Canceled'].map(
|
_task.value.status = status
|
||||||
(status) => {
|
|
||||||
return {
|
|
||||||
icon: () => h(TaskStatusIcon, { status }),
|
|
||||||
label: status,
|
|
||||||
onClick: () => {
|
|
||||||
_task.value.status = status
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function priorityOptions() {
|
function updateTaskPriority(priority) {
|
||||||
return ['Low', 'Medium', 'High'].map((priority) => {
|
_task.value.priority = priority
|
||||||
return {
|
|
||||||
label: priority,
|
|
||||||
icon: () => h(TaskPriorityIcon, { priority }),
|
|
||||||
onClick: () => {
|
|
||||||
_task.value.priority = priority
|
|
||||||
},
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateTask(close) {
|
async function updateTask(close) {
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
import IndicatorIcon from '@/components/Icons/IndicatorIcon.vue'
|
import IndicatorIcon from '@/components/Icons/IndicatorIcon.vue'
|
||||||
|
import TaskStatusIcon from '@/components/Icons/TaskStatusIcon.vue'
|
||||||
|
import TaskPriorityIcon from '@/components/Icons/TaskPriorityIcon.vue'
|
||||||
import { usersStore } from '@/stores/users'
|
import { usersStore } from '@/stores/users'
|
||||||
import { useDateFormat, useTimeAgo } from '@vueuse/core'
|
import { useDateFormat, useTimeAgo } from '@vueuse/core'
|
||||||
import { toast } from 'frappe-ui'
|
import { toast } from 'frappe-ui'
|
||||||
@ -92,6 +94,28 @@ export function statusDropdownOptions(data, doctype, action) {
|
|||||||
return options
|
return options
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function taskStatusOptions(action, data) {
|
||||||
|
return ['Backlog', 'Todo', 'In Progress', 'Done', 'Canceled'].map(
|
||||||
|
(status) => {
|
||||||
|
return {
|
||||||
|
icon: () => h(TaskStatusIcon, { status }),
|
||||||
|
label: status,
|
||||||
|
onClick: () => action && action(status, data),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function taskPriorityOptions(action, data) {
|
||||||
|
return ['Low', 'Medium', 'High'].map((priority) => {
|
||||||
|
return {
|
||||||
|
label: priority,
|
||||||
|
icon: () => h(TaskPriorityIcon, { priority }),
|
||||||
|
onClick: () => action && action(priority, data),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export function openWebsite(url) {
|
export function openWebsite(url) {
|
||||||
window.open(url, '_blank')
|
window.open(url, '_blank')
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user