Merge pull request #182 from frappe/develop
chore: Merged develop to main
This commit is contained in:
commit
ad2bf79f1d
@ -110,9 +110,10 @@ def get_deal_activities(name):
|
||||
}
|
||||
activities.append(activity)
|
||||
|
||||
for communication in docinfo.communications:
|
||||
for communication in docinfo.communications + docinfo.automated_messages:
|
||||
activity = {
|
||||
"activity_type": "communication",
|
||||
"communication_type": communication.communication_type,
|
||||
"creation": communication.creation,
|
||||
"data": {
|
||||
"subject": communication.subject,
|
||||
@ -222,9 +223,10 @@ def get_lead_activities(name):
|
||||
}
|
||||
activities.append(activity)
|
||||
|
||||
for communication in docinfo.communications:
|
||||
for communication in docinfo.communications + docinfo.automated_messages:
|
||||
activity = {
|
||||
"activity_type": "communication",
|
||||
"communication_type": communication.communication_type,
|
||||
"creation": communication.creation,
|
||||
"data": {
|
||||
"subject": communication.subject,
|
||||
|
||||
@ -108,6 +108,8 @@ class CRMDeal(Document):
|
||||
"""
|
||||
sla = get_sla(self)
|
||||
if not sla:
|
||||
self.first_responded_on = None
|
||||
self.first_response_time = None
|
||||
return
|
||||
self.sla = sla.name
|
||||
|
||||
|
||||
@ -234,6 +234,8 @@ class CRMLead(Document):
|
||||
"""
|
||||
sla = get_sla(self)
|
||||
if not sla:
|
||||
self.first_responded_on = None
|
||||
self.first_response_time = None
|
||||
return
|
||||
self.sla = sla.name
|
||||
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
<template>
|
||||
<div class="flex items-center justify-between mx-10 mt-8 mb-4 text-lg font-medium">
|
||||
<div
|
||||
class="mx-10 mb-4 mt-8 flex items-center justify-between text-lg font-medium"
|
||||
>
|
||||
<div class="flex h-8 items-center text-xl font-semibold text-gray-800">
|
||||
{{ __(title) }}
|
||||
</div>
|
||||
@ -71,8 +73,9 @@
|
||||
<LoadingIndicator class="h-6 w-6" />
|
||||
<span>{{ __('Loading...') }}</span>
|
||||
</div>
|
||||
<div
|
||||
<FadedScrollableDiv
|
||||
v-else-if="title == 'WhatsApp' && whatsappMessages.data?.length"
|
||||
:maskHeight="30"
|
||||
class="activities flex-1 overflow-y-auto"
|
||||
>
|
||||
<WhatsAppArea
|
||||
@ -81,8 +84,12 @@
|
||||
v-model:reply="replyMessage"
|
||||
:messages="whatsappMessages.data"
|
||||
/>
|
||||
</div>
|
||||
<div v-else-if="activities?.length" class="activities flex-1 overflow-y-auto">
|
||||
</FadedScrollableDiv>
|
||||
<FadedScrollableDiv
|
||||
v-else-if="activities?.length"
|
||||
:maskHeight="30"
|
||||
class="activities flex-1 overflow-y-auto"
|
||||
>
|
||||
<div
|
||||
v-if="title == 'Notes'"
|
||||
class="activity grid grid-cols-1 gap-4 px-10 pb-5 lg:grid-cols-2 xl:grid-cols-3"
|
||||
@ -386,6 +393,12 @@
|
||||
{{ __(timeAgo(activity.creation)) }}
|
||||
</div>
|
||||
</Tooltip>
|
||||
<Badge
|
||||
v-if="activity.communication_type == 'Automated Message'"
|
||||
:label="__('Notification')"
|
||||
variant="subtle"
|
||||
theme="green"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex gap-0.5">
|
||||
<Tooltip :text="__('Reply')">
|
||||
@ -433,7 +446,8 @@
|
||||
</span>
|
||||
<span v-if="activity.data.bcc">{{ activity.data.bcc }}</span>
|
||||
</div>
|
||||
<div
|
||||
<FadedScrollableDiv
|
||||
:maskHeight="30"
|
||||
class="email-content prose-f max-h-[500px] overflow-y-auto"
|
||||
v-html="activity.data.content"
|
||||
/>
|
||||
@ -720,7 +734,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</FadedScrollableDiv>
|
||||
<div
|
||||
v-else
|
||||
class="flex flex-1 flex-col items-center justify-center gap-3 text-xl font-medium text-gray-500"
|
||||
@ -812,6 +826,7 @@ import OutboundCallIcon from '@/components/Icons/OutboundCallIcon.vue'
|
||||
import ReplyIcon from '@/components/Icons/ReplyIcon.vue'
|
||||
import ReplyAllIcon from '@/components/Icons/ReplyAllIcon.vue'
|
||||
import AttachmentItem from '@/components/AttachmentItem.vue'
|
||||
import FadedScrollableDiv from '@/components/FadedScrollableDiv.vue'
|
||||
import CommunicationArea from '@/components/CommunicationArea.vue'
|
||||
import NoteModal from '@/components/Modals/NoteModal.vue'
|
||||
import TaskModal from '@/components/Modals/TaskModal.vue'
|
||||
@ -834,6 +849,7 @@ import {
|
||||
Dropdown,
|
||||
TextEditor,
|
||||
Avatar,
|
||||
Badge,
|
||||
createResource,
|
||||
call,
|
||||
} from 'frappe-ui'
|
||||
|
||||
29
frontend/src/components/FadedScrollableDiv.vue
Normal file
29
frontend/src/components/FadedScrollableDiv.vue
Normal file
@ -0,0 +1,29 @@
|
||||
<template>
|
||||
<div ref="scrollableDiv" class="scrr" :style="`maskImage: ${maskStyle}`">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
maskHeight: {
|
||||
type: Number,
|
||||
default: 20,
|
||||
},
|
||||
})
|
||||
|
||||
const scrollableDiv = ref(null)
|
||||
const maskStyle = ref('none')
|
||||
|
||||
function setMaskStyle() {
|
||||
// show mask only if div is scrollable
|
||||
if (scrollableDiv.value.scrollHeight > scrollableDiv.value.clientHeight) {
|
||||
maskStyle.value = `linear-gradient(to bottom, black calc(100% - ${props.maskHeight}px), transparent 100%);`
|
||||
} else {
|
||||
maskStyle.value = 'none'
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => setMaskStyle())
|
||||
</script>
|
||||
@ -54,16 +54,21 @@
|
||||
></audio>
|
||||
</div>
|
||||
<div
|
||||
class="max-h-30 min-h-16 w-full cursor-pointer overflow-hidden rounded border px-2 py-1.5 text-base text-gray-700"
|
||||
class="w-full cursor-pointer rounded border px-2 pt-1.5 text-base text-gray-700"
|
||||
v-else-if="field.name == 'note'"
|
||||
@click="() => (showNoteModal = true)"
|
||||
>
|
||||
<div
|
||||
v-if="field.value?.title"
|
||||
:class="[field.value?.content ? 'mb-1 font-bold' : '']"
|
||||
v-html="field.value?.title"
|
||||
/>
|
||||
<div v-if="field.value?.content" v-html="field.value?.content" />
|
||||
<FadedScrollableDiv class="max-h-24 min-h-16 overflow-y-auto">
|
||||
<div
|
||||
v-if="field.value?.title"
|
||||
:class="[field.value?.content ? 'mb-1 font-bold' : '']"
|
||||
v-html="field.value?.title"
|
||||
/>
|
||||
<div
|
||||
v-if="field.value?.content"
|
||||
v-html="field.value?.content"
|
||||
/>
|
||||
</FadedScrollableDiv>
|
||||
</div>
|
||||
<div v-else :class="field.color ? `text-${field.color}-600` : ''">
|
||||
{{ field.value }}
|
||||
@ -103,7 +108,14 @@ import CalendarIcon from '@/components/Icons/CalendarIcon.vue'
|
||||
import NoteIcon from '@/components/Icons/NoteIcon.vue'
|
||||
import CheckCircleIcon from '@/components/Icons/CheckCircleIcon.vue'
|
||||
import NoteModal from '@/components/Modals/NoteModal.vue'
|
||||
import { FeatherIcon, Avatar, Tooltip, createDocumentResource, call } from 'frappe-ui'
|
||||
import FadedScrollableDiv from '@/components/FadedScrollableDiv.vue'
|
||||
import {
|
||||
FeatherIcon,
|
||||
Avatar,
|
||||
Tooltip,
|
||||
createDocumentResource,
|
||||
call,
|
||||
} from 'frappe-ui'
|
||||
import { ref, computed, h, watch } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
|
||||
@ -52,13 +52,10 @@
|
||||
>
|
||||
<div class="mt-1 flex items-center gap-2.5">
|
||||
<div
|
||||
class="h-[5px] w-[5px] rounded-full"
|
||||
class="size-[5px] rounded-full"
|
||||
:class="[n.read ? 'bg-transparent' : 'bg-gray-900']"
|
||||
/>
|
||||
<WhatsAppIcon
|
||||
v-if="n.type == 'WhatsApp'"
|
||||
class="size-7 rounded-full"
|
||||
/>
|
||||
<WhatsAppIcon v-if="n.type == 'WhatsApp'" class="size-7" />
|
||||
<UserAvatar v-else :user="n.from_user.name" size="lg" />
|
||||
</div>
|
||||
<div>
|
||||
|
||||
@ -38,6 +38,10 @@ function startResize() {
|
||||
document.addEventListener('mouseup', () => {
|
||||
document.body.classList.remove('select-none')
|
||||
document.body.classList.remove('cursor-col-resize')
|
||||
document.querySelectorAll('.select-text1').forEach((el) => {
|
||||
el.classList.remove('select-text1')
|
||||
el.classList.add('select-text')
|
||||
})
|
||||
localStorage.setItem('sidebarWidth', sidebarWidth.value)
|
||||
sidebarResizing.value = false
|
||||
document.removeEventListener('mousemove', resize)
|
||||
@ -47,6 +51,10 @@ function resize(e) {
|
||||
sidebarResizing.value = true
|
||||
document.body.classList.add('select-none')
|
||||
document.body.classList.add('cursor-col-resize')
|
||||
document.querySelectorAll('.select-text').forEach((el) => {
|
||||
el.classList.remove('select-text')
|
||||
el.classList.add('select-text1')
|
||||
})
|
||||
sidebarWidth.value =
|
||||
props.side == 'left' ? e.clientX : window.innerWidth - e.clientX
|
||||
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
<template>
|
||||
<div class="flex max-h-[300px] flex-col gap-1.5 overflow-y-auto">
|
||||
<FadedScrollableDiv
|
||||
class="flex max-h-[300px] flex-col gap-1.5 overflow-y-auto"
|
||||
>
|
||||
<div
|
||||
v-for="field in _fields"
|
||||
:key="field.label"
|
||||
@ -105,10 +107,11 @@
|
||||
@click="field.link(data[field.name])"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</FadedScrollableDiv>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import FadedScrollableDiv from '@/components/FadedScrollableDiv.vue'
|
||||
import ArrowUpRightIcon from '@/components/Icons/ArrowUpRightIcon.vue'
|
||||
import Link from '@/components/Controls/Link.vue'
|
||||
import UserAvatar from '@/components/UserAvatar.vue'
|
||||
@ -168,6 +171,10 @@ function evaluate(code, context = {}) {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.form-control {
|
||||
margin: 2px;
|
||||
}
|
||||
|
||||
:deep(.form-control input:not([type='checkbox'])),
|
||||
:deep(.form-control select),
|
||||
:deep(.form-control textarea),
|
||||
|
||||
@ -43,7 +43,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="inline-flex gap-2">
|
||||
<div class="flex gap-2 justify-between">
|
||||
<div
|
||||
class="absolute -right-0.5 -top-0.5 flex cursor-pointer gap-1 rounded-full bg-white pb-2 pl-2 pr-1.5 pt-1.5 opacity-0 group-hover/message:opacity-100"
|
||||
:style="{
|
||||
|
||||
@ -45,7 +45,7 @@
|
||||
class="flex flex-col items-center gap-3 text-xl font-medium text-gray-500"
|
||||
>
|
||||
<PhoneIcon class="h-10 w-10" />
|
||||
<span>{{ __('No Logs Found') }}</span>
|
||||
<span>{{ __('No {0} Found', [__('Logs')]) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<CallLogModal
|
||||
|
||||
@ -51,7 +51,7 @@
|
||||
class="flex flex-col items-center gap-3 text-xl font-medium text-gray-500"
|
||||
>
|
||||
<ContactsIcon class="h-10 w-10" />
|
||||
<span>{{ __('No Contacts Found') }}</span>
|
||||
<span>{{ __('No {0} Found', [__('Contacts')]) }}</span>
|
||||
<Button :label="__('Create')" @click="showContactModal = true">
|
||||
<template #prefix><FeatherIcon name="plus" class="h-4" /></template>
|
||||
</Button>
|
||||
|
||||
@ -64,7 +64,7 @@
|
||||
</div>
|
||||
</Tooltip>
|
||||
<div class="flex flex-col gap-2.5 truncate">
|
||||
<Tooltip :text="organization?.name">
|
||||
<Tooltip :text="organization?.name || __('Set an organization')">
|
||||
<div class="truncate text-2xl font-medium">
|
||||
{{ organization?.name || __('Untitled') }}
|
||||
</div>
|
||||
|
||||
@ -48,7 +48,7 @@
|
||||
class="flex flex-col items-center gap-3 text-xl font-medium text-gray-500"
|
||||
>
|
||||
<DealsIcon class="h-10 w-10" />
|
||||
<span>{{ __('No Deals Found') }}</span>
|
||||
<span>{{ __('No {0} Found', [__('Deals')]) }}</span>
|
||||
<Button :label="__('Create')" @click="showDealModal = true">
|
||||
<template #prefix><FeatherIcon name="plus" class="h-4" /></template>
|
||||
</Button>
|
||||
|
||||
@ -52,7 +52,7 @@
|
||||
class="flex flex-col items-center gap-3 text-xl font-medium text-gray-500"
|
||||
>
|
||||
<EmailIcon class="h-10 w-10" />
|
||||
<span>{{ __('No Email Templates Found') }}</span>
|
||||
<span>{{ __('No {0} Found', [__('Email Templates')]) }}</span>
|
||||
<Button :label="__('Create')" @click="showEmailTemplateModal = true">
|
||||
<template #prefix><FeatherIcon name="plus" class="h-4" /></template>
|
||||
</Button>
|
||||
|
||||
@ -95,7 +95,7 @@
|
||||
class="!absolute bottom-0 left-0 right-0"
|
||||
>
|
||||
<div
|
||||
class="z-1 absolute bottom-0 left-0 right-0 flex h-9 cursor-pointer items-center justify-center rounded-b-full bg-black bg-opacity-40 pt-3 opacity-0 duration-300 ease-in-out group-hover:opacity-100"
|
||||
class="z-1 absolute bottom-0.5 left-0 right-0.5 flex h-9 cursor-pointer items-center justify-center rounded-b-full bg-black bg-opacity-40 pt-3 opacity-0 duration-300 ease-in-out group-hover:opacity-100"
|
||||
style="
|
||||
-webkit-clip-path: inset(12px 0 0 0);
|
||||
clip-path: inset(12px 0 0 0);
|
||||
@ -106,7 +106,7 @@
|
||||
</component>
|
||||
</div>
|
||||
<div class="flex flex-col gap-2.5 truncate">
|
||||
<Tooltip :text="lead.data.lead_name">
|
||||
<Tooltip :text="lead.data.lead_name || __('Set first name')">
|
||||
<div class="truncate text-2xl font-medium">
|
||||
{{ lead.data.lead_name || __('Untitled') }}
|
||||
</div>
|
||||
|
||||
@ -49,7 +49,7 @@
|
||||
class="flex flex-col items-center gap-3 text-xl font-medium text-gray-500"
|
||||
>
|
||||
<LeadsIcon class="h-10 w-10" />
|
||||
<span>{{ __('No Leads Found') }}</span>
|
||||
<span>{{ __('No {0} Found', [__('Leads')]) }}</span>
|
||||
<Button :label="__('Create')" @click="showLeadModal = true">
|
||||
<template #prefix><FeatherIcon name="plus" class="h-4" /></template>
|
||||
</Button>
|
||||
|
||||
@ -88,7 +88,7 @@
|
||||
class="flex flex-col items-center gap-3 text-xl font-medium text-gray-500"
|
||||
>
|
||||
<NoteIcon class="h-10 w-10" />
|
||||
<span>{{ __('No Notes Found') }}</span>
|
||||
<span>{{ __('No {0} Found', [__('Notes')]) }}</span>
|
||||
<Button :label="__('Create')" @click="createNote">
|
||||
<template #prefix><FeatherIcon name="plus" class="h-4" /></template>
|
||||
</Button>
|
||||
|
||||
@ -51,7 +51,7 @@
|
||||
class="flex flex-col items-center gap-3 text-xl font-medium text-gray-500"
|
||||
>
|
||||
<OrganizationsIcon class="h-10 w-10" />
|
||||
<span>{{ __('No Organizations Found') }}</span>
|
||||
<span>{{ __('No {0} Found', [__('Organizations')]) }}</span>
|
||||
<Button :label="__('Create')" @click="showOrganizationModal = true">
|
||||
<template #prefix><FeatherIcon name="plus" class="h-4" /></template>
|
||||
</Button>
|
||||
|
||||
@ -45,7 +45,7 @@
|
||||
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>
|
||||
<span>{{ __('No {0} Found', [__('Tasks')]) }}</span>
|
||||
<Button :label="__('Create')" @click="showTaskModal = true">
|
||||
<template #prefix><FeatherIcon name="plus" class="h-4" /></template>
|
||||
</Button>
|
||||
|
||||
@ -150,7 +150,7 @@ export function errorMessage(title, message) {
|
||||
|
||||
export function copyToClipboard(text) {
|
||||
if (navigator.clipboard && window.isSecureContext) {
|
||||
navigator.clipboard.writeText(string).then(show_success_alert)
|
||||
navigator.clipboard.writeText(text).then(show_success_alert)
|
||||
} else {
|
||||
let input = document.createElement('textarea')
|
||||
document.body.appendChild(input)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user