refactor: show call log detail in call log modal
This commit is contained in:
parent
e7ca90df51
commit
f5fa47bae0
@ -84,7 +84,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template
|
<template
|
||||||
v-if="callLog.type.label == 'Incoming' && !callLog.reference_docname"
|
v-if="
|
||||||
|
callLog.doc?.type.label == 'Incoming' && !callLog.doc?.reference_docname
|
||||||
|
"
|
||||||
#actions
|
#actions
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
@ -116,11 +118,12 @@ import {
|
|||||||
createDocumentResource,
|
createDocumentResource,
|
||||||
call,
|
call,
|
||||||
} from 'frappe-ui'
|
} from 'frappe-ui'
|
||||||
|
import { getCallLogDetail } from '@/utils/callLog'
|
||||||
import { ref, computed, h, watch } from 'vue'
|
import { ref, computed, h, watch } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
callLog: {
|
name: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: {},
|
default: {},
|
||||||
},
|
},
|
||||||
@ -130,60 +133,66 @@ const show = defineModel()
|
|||||||
const showNoteModal = ref(false)
|
const showNoteModal = ref(false)
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const callNoteDoc = ref(null)
|
const callNoteDoc = ref(null)
|
||||||
|
const callLog = ref({})
|
||||||
|
|
||||||
const detailFields = computed(() => {
|
const detailFields = computed(() => {
|
||||||
|
if (!callLog.value.doc) return []
|
||||||
let details = [
|
let details = [
|
||||||
{
|
{
|
||||||
icon: h(FeatherIcon, {
|
icon: h(FeatherIcon, {
|
||||||
name: props.callLog.type.icon,
|
name: callLog.value.doc.type.icon,
|
||||||
class: 'h-3.5 w-3.5',
|
class: 'h-3.5 w-3.5',
|
||||||
}),
|
}),
|
||||||
name: 'type',
|
name: 'type',
|
||||||
value: props.callLog.type.label + ' Call',
|
value: callLog.value.doc.type.label + ' Call',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: ContactsIcon,
|
icon: ContactsIcon,
|
||||||
name: 'receiver',
|
name: 'receiver',
|
||||||
value: {
|
value: {
|
||||||
receiver: props.callLog.receiver,
|
receiver: callLog.value.doc.receiver,
|
||||||
caller: props.callLog.caller,
|
caller: callLog.value.doc.caller,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon:
|
icon:
|
||||||
props.callLog.reference_doctype == 'CRM Lead' ? LeadsIcon : Dealsicon,
|
callLog.value.doc.reference_doctype == 'CRM Lead'
|
||||||
|
? LeadsIcon
|
||||||
|
: Dealsicon,
|
||||||
name: 'reference_doctype',
|
name: 'reference_doctype',
|
||||||
value: props.callLog.reference_doctype == 'CRM Lead' ? 'Lead' : 'Deal',
|
value:
|
||||||
|
callLog.value.doc.reference_doctype == 'CRM Lead' ? 'Lead' : 'Deal',
|
||||||
link: () => {
|
link: () => {
|
||||||
if (props.callLog.reference_doctype == 'CRM Lead') {
|
if (callLog.value.doc.reference_doctype == 'CRM Lead') {
|
||||||
router.push({
|
router.push({
|
||||||
name: 'Lead',
|
name: 'Lead',
|
||||||
params: { leadId: props.callLog.reference_docname },
|
params: { leadId: callLog.value.doc.reference_docname },
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
router.push({
|
router.push({
|
||||||
name: 'Deal',
|
name: 'Deal',
|
||||||
params: { dealId: props.callLog.reference_docname },
|
params: { dealId: callLog.value.doc.reference_docname },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
condition: () => callLog.value.doc.reference_docname,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: CalendarIcon,
|
icon: CalendarIcon,
|
||||||
name: 'creation',
|
name: 'creation',
|
||||||
value: props.callLog.creation.label,
|
value: callLog.value.doc.creation.label,
|
||||||
tooltip: props.callLog.creation.label,
|
tooltip: callLog.value.doc.creation.label,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: DurationIcon,
|
icon: DurationIcon,
|
||||||
name: 'duration',
|
name: 'duration',
|
||||||
value: props.callLog.duration.label,
|
value: callLog.value.doc.duration.label,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: CheckCircleIcon,
|
icon: CheckCircleIcon,
|
||||||
name: 'status',
|
name: 'status',
|
||||||
value: props.callLog.status.label,
|
value: callLog.value.doc.status.label,
|
||||||
color: props.callLog.status.color,
|
color: callLog.value.doc.status.color,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: h(FeatherIcon, {
|
icon: h(FeatherIcon, {
|
||||||
@ -191,7 +200,7 @@ const detailFields = computed(() => {
|
|||||||
class: 'h-4 w-4 mt-2',
|
class: 'h-4 w-4 mt-2',
|
||||||
}),
|
}),
|
||||||
name: 'recording_url',
|
name: 'recording_url',
|
||||||
value: props.callLog.recording_url,
|
value: callLog.value.doc.recording_url,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: NoteIcon,
|
icon: NoteIcon,
|
||||||
@ -200,12 +209,14 @@ const detailFields = computed(() => {
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
return details.filter((detail) => detail.value)
|
return details
|
||||||
|
.filter((detail) => detail.value)
|
||||||
|
.filter((detail) => (detail.condition ? detail.condition() : true))
|
||||||
})
|
})
|
||||||
|
|
||||||
function createLead() {
|
function createLead() {
|
||||||
call('crm.fcrm.doctype.crm_call_log.crm_call_log.create_lead_from_call_log', {
|
call('crm.fcrm.doctype.crm_call_log.crm_call_log.create_lead_from_call_log', {
|
||||||
call_log: props.callLog,
|
call_log: callLog.value.doc,
|
||||||
}).then((d) => {
|
}).then((d) => {
|
||||||
if (d) {
|
if (d) {
|
||||||
router.push({ name: 'Lead', params: { leadId: d } })
|
router.push({ name: 'Lead', params: { leadId: d } })
|
||||||
@ -215,12 +226,45 @@ function createLead() {
|
|||||||
|
|
||||||
watch(show, (val) => {
|
watch(show, (val) => {
|
||||||
if (val) {
|
if (val) {
|
||||||
callNoteDoc.value = createDocumentResource({
|
callLog.value = createDocumentResource({
|
||||||
doctype: 'FCRM Note',
|
doctype: 'CRM Call Log',
|
||||||
name: props.callLog.note,
|
name: props.name,
|
||||||
fields: ['title', 'content'],
|
fields: [
|
||||||
cache: ['note', props.callLog.note],
|
'name',
|
||||||
|
'caller',
|
||||||
|
'receiver',
|
||||||
|
'duration',
|
||||||
|
'type',
|
||||||
|
'status',
|
||||||
|
'from',
|
||||||
|
'to',
|
||||||
|
'note',
|
||||||
|
'recording_url',
|
||||||
|
'reference_doctype',
|
||||||
|
'reference_docname',
|
||||||
|
'creation',
|
||||||
|
],
|
||||||
|
cache: ['call_log', props.name],
|
||||||
auto: true,
|
auto: true,
|
||||||
|
transform: (doc) => {
|
||||||
|
for (const key in doc) {
|
||||||
|
doc[key] = getCallLogDetail(key, doc)
|
||||||
|
}
|
||||||
|
return doc
|
||||||
|
},
|
||||||
|
onSuccess: (doc) => {
|
||||||
|
if (!doc.note) {
|
||||||
|
callNoteDoc.value = null
|
||||||
|
return
|
||||||
|
}
|
||||||
|
callNoteDoc.value = createDocumentResource({
|
||||||
|
doctype: 'FCRM Note',
|
||||||
|
name: doc.note,
|
||||||
|
fields: ['title', 'content'],
|
||||||
|
cache: ['note', doc.note],
|
||||||
|
auto: true,
|
||||||
|
})
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@ -53,7 +53,7 @@
|
|||||||
<CallLogModal
|
<CallLogModal
|
||||||
v-model="showCallLogModal"
|
v-model="showCallLogModal"
|
||||||
v-model:reloadCallLogs="callLogs"
|
v-model:reloadCallLogs="callLogs"
|
||||||
:callLog="callLog"
|
:name="selectedCallLog"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -64,20 +64,10 @@ import LayoutHeader from '@/components/LayoutHeader.vue'
|
|||||||
import ViewControls from '@/components/ViewControls.vue'
|
import ViewControls from '@/components/ViewControls.vue'
|
||||||
import CallLogsListView from '@/components/ListViews/CallLogsListView.vue'
|
import CallLogsListView from '@/components/ListViews/CallLogsListView.vue'
|
||||||
import CallLogModal from '@/components/Modals/CallLogModal.vue'
|
import CallLogModal from '@/components/Modals/CallLogModal.vue'
|
||||||
import {
|
import { getCallLogDetail } from '@/utils/callLog'
|
||||||
secondsToDuration,
|
|
||||||
dateFormat,
|
|
||||||
dateTooltipFormat,
|
|
||||||
timeAgo,
|
|
||||||
} from '@/utils'
|
|
||||||
import { usersStore } from '@/stores/users'
|
|
||||||
import { contactsStore } from '@/stores/contacts'
|
|
||||||
import { Breadcrumbs } from 'frappe-ui'
|
import { Breadcrumbs } from 'frappe-ui'
|
||||||
import { computed, ref } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
|
|
||||||
const { getUser } = usersStore()
|
|
||||||
const { getContact, getLeadContact } = contactsStore()
|
|
||||||
|
|
||||||
const breadcrumbs = [{ label: __('Call Logs'), route: { name: 'Call Logs' } }]
|
const breadcrumbs = [{ label: __('Call Logs'), route: { name: 'Call Logs' } }]
|
||||||
|
|
||||||
const callLogsListView = ref(null)
|
const callLogsListView = ref(null)
|
||||||
@ -94,119 +84,17 @@ const rows = computed(() => {
|
|||||||
return callLogs.value?.data.data.map((callLog) => {
|
return callLogs.value?.data.data.map((callLog) => {
|
||||||
let _rows = {}
|
let _rows = {}
|
||||||
callLogs.value?.data.rows.forEach((row) => {
|
callLogs.value?.data.rows.forEach((row) => {
|
||||||
_rows[row] = callLog[row]
|
_rows[row] = getCallLogDetail(row, callLog)
|
||||||
|
|
||||||
let incoming = callLog.type === 'Incoming'
|
|
||||||
|
|
||||||
if (row === 'caller') {
|
|
||||||
_rows[row] = {
|
|
||||||
label: incoming
|
|
||||||
? getContact(callLog.from)?.full_name ||
|
|
||||||
getLeadContact(callLog.from)?.full_name ||
|
|
||||||
'Unknown'
|
|
||||||
: getUser(callLog.caller).full_name,
|
|
||||||
image: incoming
|
|
||||||
? getContact(callLog.from)?.image ||
|
|
||||||
getLeadContact(callLog.from)?.image
|
|
||||||
: getUser(callLog.caller).user_image,
|
|
||||||
}
|
|
||||||
} else if (row === 'receiver') {
|
|
||||||
_rows[row] = {
|
|
||||||
label: incoming
|
|
||||||
? getUser(callLog.receiver).full_name
|
|
||||||
: getContact(callLog.to)?.full_name ||
|
|
||||||
getLeadContact(callLog.to)?.full_name ||
|
|
||||||
'Unknown',
|
|
||||||
image: incoming
|
|
||||||
? getUser(callLog.receiver).user_image
|
|
||||||
: getContact(callLog.to)?.image ||
|
|
||||||
getLeadContact(callLog.to)?.image,
|
|
||||||
}
|
|
||||||
} else if (row === 'duration') {
|
|
||||||
_rows[row] = {
|
|
||||||
label: secondsToDuration(callLog.duration),
|
|
||||||
icon: 'clock',
|
|
||||||
}
|
|
||||||
} else if (row === 'type') {
|
|
||||||
_rows[row] = {
|
|
||||||
label: callLog.type,
|
|
||||||
icon: incoming ? 'phone-incoming' : 'phone-outgoing',
|
|
||||||
}
|
|
||||||
} else if (row === 'status') {
|
|
||||||
_rows[row] = {
|
|
||||||
label: statusLabelMap[callLog.status],
|
|
||||||
color: statusColorMap[callLog.status],
|
|
||||||
}
|
|
||||||
} else if (['modified', 'creation'].includes(row)) {
|
|
||||||
_rows[row] = {
|
|
||||||
label: dateFormat(callLog[row], dateTooltipFormat),
|
|
||||||
timeAgo: __(timeAgo(callLog[row])),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
return _rows
|
return _rows
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
const showCallLogModal = ref(false)
|
const showCallLogModal = ref(false)
|
||||||
|
const selectedCallLog = ref(null)
|
||||||
const callLog = ref({
|
|
||||||
name: '',
|
|
||||||
caller: '',
|
|
||||||
receiver: '',
|
|
||||||
duration: '',
|
|
||||||
type: '',
|
|
||||||
status: '',
|
|
||||||
from: '',
|
|
||||||
to: '',
|
|
||||||
note: '',
|
|
||||||
recording_url: '',
|
|
||||||
reference_doctype: '',
|
|
||||||
reference_docname: '',
|
|
||||||
creation: '',
|
|
||||||
})
|
|
||||||
|
|
||||||
function showCallLog(name) {
|
function showCallLog(name) {
|
||||||
let d = rows.value?.find((row) => row.name === name)
|
selectedCallLog.value = name
|
||||||
callLog.value = {
|
|
||||||
name: d.name,
|
|
||||||
caller: d.caller,
|
|
||||||
receiver: d.receiver,
|
|
||||||
duration: d.duration,
|
|
||||||
type: d.type,
|
|
||||||
status: d.status,
|
|
||||||
from: d.from,
|
|
||||||
to: d.to,
|
|
||||||
note: d.note,
|
|
||||||
recording_url: d.recording_url,
|
|
||||||
reference_doctype: d.reference_doctype,
|
|
||||||
reference_docname: d.reference_docname,
|
|
||||||
creation: d.creation,
|
|
||||||
}
|
|
||||||
showCallLogModal.value = true
|
showCallLogModal.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
const statusLabelMap = {
|
|
||||||
Completed: 'Completed',
|
|
||||||
Initiated: 'Initiated',
|
|
||||||
Busy: 'Declined',
|
|
||||||
Failed: 'Failed',
|
|
||||||
Queued: 'Queued',
|
|
||||||
Cancelled: 'Cancelled',
|
|
||||||
Ringing: 'Ringing',
|
|
||||||
'No Answer': 'Missed Call',
|
|
||||||
'In Progress': 'In Progress',
|
|
||||||
}
|
|
||||||
|
|
||||||
const statusColorMap = {
|
|
||||||
Completed: 'green',
|
|
||||||
Busy: 'orange',
|
|
||||||
Failed: 'red',
|
|
||||||
Initiated: 'gray',
|
|
||||||
Queued: 'gray',
|
|
||||||
Cancelled: 'gray',
|
|
||||||
Ringing: 'gray',
|
|
||||||
'No Answer': 'red',
|
|
||||||
'In Progress': 'blue',
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
84
frontend/src/utils/callLog.js
Normal file
84
frontend/src/utils/callLog.js
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
import {
|
||||||
|
secondsToDuration,
|
||||||
|
dateFormat,
|
||||||
|
dateTooltipFormat,
|
||||||
|
timeAgo,
|
||||||
|
} from '@/utils'
|
||||||
|
import { usersStore } from '@/stores/users'
|
||||||
|
import { contactsStore } from '@/stores/contacts'
|
||||||
|
|
||||||
|
const { getUser } = usersStore()
|
||||||
|
const { getContact, getLeadContact } = contactsStore()
|
||||||
|
|
||||||
|
export function getCallLogDetail(row, log) {
|
||||||
|
let incoming = log.type === 'Incoming'
|
||||||
|
|
||||||
|
if (row === 'caller') {
|
||||||
|
return {
|
||||||
|
label: incoming
|
||||||
|
? getContact(log.from)?.full_name ||
|
||||||
|
getLeadContact(log.from)?.full_name ||
|
||||||
|
'Unknown'
|
||||||
|
: getUser(log.caller).full_name,
|
||||||
|
image: incoming
|
||||||
|
? getContact(log.from)?.image || getLeadContact(log.from)?.image
|
||||||
|
: getUser(log.caller).user_image,
|
||||||
|
}
|
||||||
|
} else if (row === 'receiver') {
|
||||||
|
return {
|
||||||
|
label: incoming
|
||||||
|
? getUser(log.receiver).full_name
|
||||||
|
: getContact(log.to)?.full_name ||
|
||||||
|
getLeadContact(log.to)?.full_name ||
|
||||||
|
'Unknown',
|
||||||
|
image: incoming
|
||||||
|
? getUser(log.receiver).user_image
|
||||||
|
: getContact(log.to)?.image || getLeadContact(log.to)?.image,
|
||||||
|
}
|
||||||
|
} else if (row === 'duration') {
|
||||||
|
return {
|
||||||
|
label: secondsToDuration(log.duration),
|
||||||
|
icon: 'clock',
|
||||||
|
}
|
||||||
|
} else if (row === 'type') {
|
||||||
|
return {
|
||||||
|
label: log.type,
|
||||||
|
icon: incoming ? 'phone-incoming' : 'phone-outgoing',
|
||||||
|
}
|
||||||
|
} else if (row === 'status') {
|
||||||
|
return {
|
||||||
|
label: statusLabelMap[log.status],
|
||||||
|
color: statusColorMap[log.status],
|
||||||
|
}
|
||||||
|
} else if (['modified', 'creation'].includes(row)) {
|
||||||
|
return {
|
||||||
|
label: dateFormat(log[row], dateTooltipFormat),
|
||||||
|
timeAgo: __(timeAgo(log[row])),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return log[row]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const statusLabelMap = {
|
||||||
|
Completed: 'Completed',
|
||||||
|
Initiated: 'Initiated',
|
||||||
|
Busy: 'Declined',
|
||||||
|
Failed: 'Failed',
|
||||||
|
Queued: 'Queued',
|
||||||
|
Cancelled: 'Cancelled',
|
||||||
|
Ringing: 'Ringing',
|
||||||
|
'No Answer': 'Missed Call',
|
||||||
|
'In Progress': 'In Progress',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const statusColorMap = {
|
||||||
|
Completed: 'green',
|
||||||
|
Busy: 'orange',
|
||||||
|
Failed: 'red',
|
||||||
|
Initiated: 'gray',
|
||||||
|
Queued: 'gray',
|
||||||
|
Cancelled: 'gray',
|
||||||
|
Ringing: 'gray',
|
||||||
|
'No Answer': 'red',
|
||||||
|
'In Progress': 'blue',
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user