Merge pull request #389 from frappe/develop
chore: Merge develop to main
This commit is contained in:
commit
c6edba6622
@ -18,16 +18,18 @@ def notify_mentions(doc):
|
|||||||
if not content:
|
if not content:
|
||||||
return
|
return
|
||||||
mentions = extract_mentions(content)
|
mentions = extract_mentions(content)
|
||||||
|
reference_doc = frappe.get_doc(doc.reference_doctype, doc.reference_name)
|
||||||
for mention in mentions:
|
for mention in mentions:
|
||||||
owner = frappe.get_cached_value("User", doc.owner, "full_name")
|
owner = frappe.get_cached_value("User", doc.owner, "full_name")
|
||||||
doctype = doc.reference_doctype
|
doctype = doc.reference_doctype
|
||||||
if doctype.startswith("CRM "):
|
if doctype.startswith("CRM "):
|
||||||
doctype = doctype[4:].lower()
|
doctype = doctype[4:].lower()
|
||||||
|
name = reference_doc.lead_name or name if doctype == "lead" else reference_doc.organization or reference_doc.lead_name or name
|
||||||
notification_text = f"""
|
notification_text = f"""
|
||||||
<div class="mb-2 leading-5 text-gray-600">
|
<div class="mb-2 leading-5 text-gray-600">
|
||||||
<span class="font-medium text-gray-900">{ owner }</span>
|
<span class="font-medium text-gray-900">{ owner }</span>
|
||||||
<span>{ _('mentioned you in {0}').format(doctype) }</span>
|
<span>{ _('mentioned you in {0}').format(doctype) }</span>
|
||||||
<span class="font-medium text-gray-900">{ doc.reference_name }</span>
|
<span class="font-medium text-gray-900">{ name }</span>
|
||||||
</div>
|
</div>
|
||||||
"""
|
"""
|
||||||
notify_user({
|
notify_user({
|
||||||
|
|||||||
@ -617,6 +617,7 @@ def get_field_obj(field):
|
|||||||
obj["placeholder"] = field.get("placeholder") or "Select " + field.label + "..."
|
obj["placeholder"] = field.get("placeholder") or "Select " + field.label + "..."
|
||||||
obj["doctype"] = field.options
|
obj["doctype"] = field.options
|
||||||
elif field.fieldtype == "Select" and field.options:
|
elif field.fieldtype == "Select" and field.options:
|
||||||
|
obj["placeholder"] = field.get("placeholder") or "Select " + field.label + "..."
|
||||||
obj["options"] = [{"label": option, "value": option} for option in field.options.split("\n")]
|
obj["options"] = [{"label": option, "value": option} for option in field.options.split("\n")]
|
||||||
|
|
||||||
if field.read_only:
|
if field.read_only:
|
||||||
|
|||||||
@ -27,7 +27,7 @@ def get_notifications():
|
|||||||
"type": notification.type,
|
"type": notification.type,
|
||||||
"to_user": notification.to_user,
|
"to_user": notification.to_user,
|
||||||
"read": notification.read,
|
"read": notification.read,
|
||||||
"comment": notification.comment,
|
"hash": get_hash(notification),
|
||||||
"notification_text": notification.notification_text,
|
"notification_text": notification.notification_text,
|
||||||
"notification_type_doctype": notification.notification_type_doctype,
|
"notification_type_doctype": notification.notification_type_doctype,
|
||||||
"notification_type_doc": notification.notification_type_doc,
|
"notification_type_doc": notification.notification_type_doc,
|
||||||
@ -58,3 +58,17 @@ def mark_as_read(user=None, doc=None):
|
|||||||
d = frappe.get_doc("CRM Notification", n.name)
|
d = frappe.get_doc("CRM Notification", n.name)
|
||||||
d.read = True
|
d.read = True
|
||||||
d.save()
|
d.save()
|
||||||
|
|
||||||
|
def get_hash(notification):
|
||||||
|
_hash = ""
|
||||||
|
if notification.type == "Mention" and notification.notification_type_doc:
|
||||||
|
_hash = "#" + notification.notification_type_doc
|
||||||
|
|
||||||
|
if notification.type == "WhatsApp":
|
||||||
|
_hash = "#whatsapp"
|
||||||
|
|
||||||
|
if notification.type == "Assignment" and notification.notification_type_doctype == "CRM Task":
|
||||||
|
_hash = "#tasks"
|
||||||
|
if "has been removed by" in notification.message:
|
||||||
|
_hash = ""
|
||||||
|
return _hash
|
||||||
@ -52,8 +52,8 @@ def get_notification_text(owner, doc, reference_doc, is_cancelled=False):
|
|||||||
if doctype.startswith("CRM "):
|
if doctype.startswith("CRM "):
|
||||||
doctype = doctype[4:].lower()
|
doctype = doctype[4:].lower()
|
||||||
|
|
||||||
if doctype in ["CRM Lead", "CRM Deal"]:
|
if doctype in ["lead", "deal"]:
|
||||||
name = reference_doc.lead_name or name if doctype == "CRM Lead" else reference_doc.organization or reference_doc.lead_name or name
|
name = reference_doc.lead_name or name if doctype == "lead" else reference_doc.organization or reference_doc.lead_name or name
|
||||||
|
|
||||||
if is_cancelled:
|
if is_cancelled:
|
||||||
return f"""
|
return f"""
|
||||||
@ -76,7 +76,7 @@ def get_notification_text(owner, doc, reference_doc, is_cancelled=False):
|
|||||||
</div>
|
</div>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if doc.reference_type == "CRM Task":
|
if doctype == "task":
|
||||||
if is_cancelled:
|
if is_cancelled:
|
||||||
return f"""
|
return f"""
|
||||||
<div class="mb-2 leading-5 text-gray-600">
|
<div class="mb-2 leading-5 text-gray-600">
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
Subproject commit cf4e7d347237c23ebde5f5c890abdf7e81284961
|
Subproject commit 427b76188fe8b20e683bccf9bb4003821253259f
|
||||||
@ -14,7 +14,7 @@
|
|||||||
"@vueuse/core": "^10.3.0",
|
"@vueuse/core": "^10.3.0",
|
||||||
"@vueuse/integrations": "^10.3.0",
|
"@vueuse/integrations": "^10.3.0",
|
||||||
"feather-icons": "^4.28.0",
|
"feather-icons": "^4.28.0",
|
||||||
"frappe-ui": "^0.1.66",
|
"frappe-ui": "^0.1.70",
|
||||||
"gemoji": "^8.1.0",
|
"gemoji": "^8.1.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"mime": "^4.0.1",
|
"mime": "^4.0.1",
|
||||||
|
|||||||
@ -457,10 +457,6 @@ const { getUser } = usersStore()
|
|||||||
const { getContact, getLeadContact } = contactsStore()
|
const { getContact, getLeadContact } = contactsStore()
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
title: {
|
|
||||||
type: String,
|
|
||||||
default: 'Activity',
|
|
||||||
},
|
|
||||||
doctype: {
|
doctype: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'CRM Lead',
|
default: 'CRM Lead',
|
||||||
@ -471,6 +467,8 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
const doc = defineModel()
|
const doc = defineModel()
|
||||||
const reload = defineModel('reload')
|
const reload = defineModel('reload')
|
||||||
const tabIndex = defineModel('tabIndex')
|
const tabIndex = defineModel('tabIndex')
|
||||||
@ -478,6 +476,8 @@ const tabIndex = defineModel('tabIndex')
|
|||||||
const reload_email = ref(false)
|
const reload_email = ref(false)
|
||||||
const modalRef = ref(null)
|
const modalRef = ref(null)
|
||||||
|
|
||||||
|
const title = computed(() => props.tabs?.[tabIndex.value]?.name || 'Activity')
|
||||||
|
|
||||||
const all_activities = createResource({
|
const all_activities = createResource({
|
||||||
url: 'crm.api.activities.get_activities',
|
url: 'crm.api.activities.get_activities',
|
||||||
params: { name: doc.value.data.name },
|
params: { name: doc.value.data.name },
|
||||||
@ -549,6 +549,14 @@ onMounted(() => {
|
|||||||
whatsappMessages.reload()
|
whatsappMessages.reload()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
nextTick(() => {
|
||||||
|
const hash = route.hash.slice(1) || null
|
||||||
|
let tabNames = props.tabs?.map((tab) => tab.name)
|
||||||
|
if (!tabNames?.includes(hash)) {
|
||||||
|
scroll(hash)
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
function sendTemplate(template) {
|
function sendTemplate(template) {
|
||||||
@ -577,25 +585,25 @@ function get_activities() {
|
|||||||
|
|
||||||
const activities = computed(() => {
|
const activities = computed(() => {
|
||||||
let activities = []
|
let activities = []
|
||||||
if (props.title == 'Activity') {
|
if (title.value == 'Activity') {
|
||||||
activities = get_activities()
|
activities = get_activities()
|
||||||
} else if (props.title == 'Emails') {
|
} else if (title.value == 'Emails') {
|
||||||
if (!all_activities.data?.versions) return []
|
if (!all_activities.data?.versions) return []
|
||||||
activities = all_activities.data.versions.filter(
|
activities = all_activities.data.versions.filter(
|
||||||
(activity) => activity.activity_type === 'communication',
|
(activity) => activity.activity_type === 'communication',
|
||||||
)
|
)
|
||||||
} else if (props.title == 'Comments') {
|
} else if (title.value == 'Comments') {
|
||||||
if (!all_activities.data?.versions) return []
|
if (!all_activities.data?.versions) return []
|
||||||
activities = all_activities.data.versions.filter(
|
activities = all_activities.data.versions.filter(
|
||||||
(activity) => activity.activity_type === 'comment',
|
(activity) => activity.activity_type === 'comment',
|
||||||
)
|
)
|
||||||
} else if (props.title == 'Calls') {
|
} else if (title.value == 'Calls') {
|
||||||
if (!all_activities.data?.calls) return []
|
if (!all_activities.data?.calls) return []
|
||||||
return sortByCreation(all_activities.data.calls)
|
return sortByCreation(all_activities.data.calls)
|
||||||
} else if (props.title == 'Tasks') {
|
} else if (title.value == 'Tasks') {
|
||||||
if (!all_activities.data?.tasks) return []
|
if (!all_activities.data?.tasks) return []
|
||||||
return sortByCreation(all_activities.data.tasks)
|
return sortByCreation(all_activities.data.tasks)
|
||||||
} else if (props.title == 'Notes') {
|
} else if (title.value == 'Notes') {
|
||||||
if (!all_activities.data?.notes) return []
|
if (!all_activities.data?.notes) return []
|
||||||
return sortByCreation(all_activities.data.notes)
|
return sortByCreation(all_activities.data.notes)
|
||||||
}
|
}
|
||||||
@ -649,17 +657,17 @@ function update_activities_details(activity) {
|
|||||||
|
|
||||||
const emptyText = computed(() => {
|
const emptyText = computed(() => {
|
||||||
let text = 'No Activities'
|
let text = 'No Activities'
|
||||||
if (props.title == 'Emails') {
|
if (title.value == 'Emails') {
|
||||||
text = 'No Email Communications'
|
text = 'No Email Communications'
|
||||||
} else if (props.title == 'Comments') {
|
} else if (title.value == 'Comments') {
|
||||||
text = 'No Comments'
|
text = 'No Comments'
|
||||||
} else if (props.title == 'Calls') {
|
} else if (title.value == 'Calls') {
|
||||||
text = 'No Call Logs'
|
text = 'No Call Logs'
|
||||||
} else if (props.title == 'Notes') {
|
} else if (title.value == 'Notes') {
|
||||||
text = 'No Notes'
|
text = 'No Notes'
|
||||||
} else if (props.title == 'Tasks') {
|
} else if (title.value == 'Tasks') {
|
||||||
text = 'No Tasks'
|
text = 'No Tasks'
|
||||||
} else if (props.title == 'WhatsApp') {
|
} else if (title.value == 'WhatsApp') {
|
||||||
text = 'No WhatsApp Messages'
|
text = 'No WhatsApp Messages'
|
||||||
}
|
}
|
||||||
return text
|
return text
|
||||||
@ -667,17 +675,17 @@ const emptyText = computed(() => {
|
|||||||
|
|
||||||
const emptyTextIcon = computed(() => {
|
const emptyTextIcon = computed(() => {
|
||||||
let icon = ActivityIcon
|
let icon = ActivityIcon
|
||||||
if (props.title == 'Emails') {
|
if (title.value == 'Emails') {
|
||||||
icon = Email2Icon
|
icon = Email2Icon
|
||||||
} else if (props.title == 'Comments') {
|
} else if (title.value == 'Comments') {
|
||||||
icon = CommentIcon
|
icon = CommentIcon
|
||||||
} else if (props.title == 'Calls') {
|
} else if (title.value == 'Calls') {
|
||||||
icon = PhoneIcon
|
icon = PhoneIcon
|
||||||
} else if (props.title == 'Notes') {
|
} else if (title.value == 'Notes') {
|
||||||
icon = NoteIcon
|
icon = NoteIcon
|
||||||
} else if (props.title == 'Tasks') {
|
} else if (title.value == 'Tasks') {
|
||||||
icon = TaskIcon
|
icon = TaskIcon
|
||||||
} else if (props.title == 'WhatsApp') {
|
} else if (title.value == 'WhatsApp') {
|
||||||
icon = WhatsAppIcon
|
icon = WhatsAppIcon
|
||||||
}
|
}
|
||||||
return h(icon, { class: 'text-gray-500' })
|
return h(icon, { class: 'text-gray-500' })
|
||||||
@ -720,6 +728,7 @@ watch([reload, reload_email], ([reload_value, reload_email_value]) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
function scroll(hash) {
|
function scroll(hash) {
|
||||||
|
if (['tasks', 'notes'].includes(route.hash?.slice(1))) return
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
let el
|
let el
|
||||||
if (!hash) {
|
if (!hash) {
|
||||||
@ -736,11 +745,4 @@ function scroll(hash) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
defineExpose({ emailBox })
|
defineExpose({ emailBox })
|
||||||
|
|
||||||
const route = useRoute()
|
|
||||||
|
|
||||||
nextTick(() => {
|
|
||||||
const hash = route.hash.slice(1) || null
|
|
||||||
scroll(hash)
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
:task="task"
|
:task="task"
|
||||||
:doctype="doctype"
|
:doctype="doctype"
|
||||||
:doc="doc.data?.name"
|
:doc="doc.data?.name"
|
||||||
|
@after="redirect('tasks')"
|
||||||
/>
|
/>
|
||||||
<NoteModal
|
<NoteModal
|
||||||
v-model="showNoteModal"
|
v-model="showNoteModal"
|
||||||
@ -12,6 +13,7 @@
|
|||||||
:note="note"
|
:note="note"
|
||||||
:doctype="doctype"
|
:doctype="doctype"
|
||||||
:doc="doc.data?.name"
|
:doc="doc.data?.name"
|
||||||
|
@after="redirect('notes')"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
@ -19,6 +21,7 @@ import TaskModal from '@/components/Modals/TaskModal.vue'
|
|||||||
import NoteModal from '@/components/Modals/NoteModal.vue'
|
import NoteModal from '@/components/Modals/NoteModal.vue'
|
||||||
import { call } from 'frappe-ui'
|
import { call } from 'frappe-ui'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
doctype: String,
|
doctype: String,
|
||||||
@ -74,6 +77,19 @@ function showNote(n) {
|
|||||||
showNoteModal.value = true
|
showNoteModal.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// common
|
||||||
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
function redirect(tabName) {
|
||||||
|
if (route.name == 'Lead' || route.name == 'Deal') {
|
||||||
|
let hash = '#' + tabName
|
||||||
|
if (route.hash != hash) {
|
||||||
|
router.push({ ...route, hash })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
showTask,
|
showTask,
|
||||||
deleteTask,
|
deleteTask,
|
||||||
|
|||||||
@ -38,7 +38,7 @@
|
|||||||
@update:modelValue="
|
@update:modelValue="
|
||||||
() => {
|
() => {
|
||||||
content += emoji
|
content += emoji
|
||||||
$refs.textarea.$el.focus()
|
$refs.textareaRef.el.focus()
|
||||||
capture('whatsapp_emoji_added')
|
capture('whatsapp_emoji_added')
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
@ -50,7 +50,7 @@
|
|||||||
</IconPicker>
|
</IconPicker>
|
||||||
</div>
|
</div>
|
||||||
<Textarea
|
<Textarea
|
||||||
ref="textarea"
|
ref="textareaRef"
|
||||||
type="textarea"
|
type="textarea"
|
||||||
class="min-h-8 w-full"
|
class="min-h-8 w-full"
|
||||||
:rows="rows"
|
:rows="rows"
|
||||||
@ -58,7 +58,7 @@
|
|||||||
:placeholder="placeholder"
|
:placeholder="placeholder"
|
||||||
@focus="rows = 6"
|
@focus="rows = 6"
|
||||||
@blur="rows = 1"
|
@blur="rows = 1"
|
||||||
@keydown.enter="(e) => sendTextMessage(e)"
|
@keydown.enter.stop="(e) => sendTextMessage(e)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -78,7 +78,7 @@ const doc = defineModel()
|
|||||||
const whatsapp = defineModel('whatsapp')
|
const whatsapp = defineModel('whatsapp')
|
||||||
const reply = defineModel('reply')
|
const reply = defineModel('reply')
|
||||||
const rows = ref(1)
|
const rows = ref(1)
|
||||||
const textarea = ref(null)
|
const textareaRef = ref(null)
|
||||||
const emoji = ref('')
|
const emoji = ref('')
|
||||||
|
|
||||||
const content = ref('')
|
const content = ref('')
|
||||||
@ -86,7 +86,7 @@ const placeholder = ref(__('Type your message here...'))
|
|||||||
const fileType = ref('')
|
const fileType = ref('')
|
||||||
|
|
||||||
function show() {
|
function show() {
|
||||||
nextTick(() => textarea.value.$el.focus())
|
nextTick(() => textareaRef.value.el.focus())
|
||||||
}
|
}
|
||||||
|
|
||||||
function uploadFile(file) {
|
function uploadFile(file) {
|
||||||
@ -99,7 +99,7 @@ function uploadFile(file) {
|
|||||||
function sendTextMessage(event) {
|
function sendTextMessage(event) {
|
||||||
if (event.shiftKey) return
|
if (event.shiftKey) return
|
||||||
sendWhatsAppMessage()
|
sendWhatsAppMessage()
|
||||||
textarea.value.$el.blur()
|
textareaRef.value.el?.blur()
|
||||||
content.value = ''
|
content.value = ''
|
||||||
capture('whatsapp_send_message')
|
capture('whatsapp_send_message')
|
||||||
}
|
}
|
||||||
|
|||||||
@ -48,7 +48,7 @@
|
|||||||
<FormControl
|
<FormControl
|
||||||
v-if="field.read_only && field.type !== 'Check'"
|
v-if="field.read_only && field.type !== 'Check'"
|
||||||
type="text"
|
type="text"
|
||||||
:placeholder="__(field.placeholder || field.label)"
|
:placeholder="getPlaceholder(field)"
|
||||||
v-model="data[field.name]"
|
v-model="data[field.name]"
|
||||||
:disabled="true"
|
:disabled="true"
|
||||||
/>
|
/>
|
||||||
@ -59,7 +59,7 @@
|
|||||||
:class="field.prefix ? 'prefix' : ''"
|
:class="field.prefix ? 'prefix' : ''"
|
||||||
:options="field.options"
|
:options="field.options"
|
||||||
v-model="data[field.name]"
|
v-model="data[field.name]"
|
||||||
:placeholder="__(field.placeholder || field.label)"
|
:placeholder="getPlaceholder(field)"
|
||||||
>
|
>
|
||||||
<template v-if="field.prefix" #prefix>
|
<template v-if="field.prefix" #prefix>
|
||||||
<IndicatorIcon :class="field.prefix" />
|
<IndicatorIcon :class="field.prefix" />
|
||||||
@ -91,7 +91,7 @@
|
|||||||
:doctype="field.options"
|
:doctype="field.options"
|
||||||
:filters="field.filters"
|
:filters="field.filters"
|
||||||
@change="(v) => (data[field.name] = v)"
|
@change="(v) => (data[field.name] = v)"
|
||||||
:placeholder="__(field.placeholder || field.label)"
|
:placeholder="getPlaceholder(field)"
|
||||||
:onCreate="field.create"
|
:onCreate="field.create"
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
@ -113,7 +113,7 @@
|
|||||||
:doctype="field.options"
|
:doctype="field.options"
|
||||||
:filters="field.filters"
|
:filters="field.filters"
|
||||||
@change="(v) => (data[field.name] = v)"
|
@change="(v) => (data[field.name] = v)"
|
||||||
:placeholder="__(field.placeholder || field.label)"
|
:placeholder="getPlaceholder(field)"
|
||||||
:hideMe="true"
|
:hideMe="true"
|
||||||
>
|
>
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
@ -182,13 +182,13 @@
|
|||||||
<DateTimePicker
|
<DateTimePicker
|
||||||
v-else-if="field.type === 'Datetime'"
|
v-else-if="field.type === 'Datetime'"
|
||||||
v-model="data[field.name]"
|
v-model="data[field.name]"
|
||||||
:placeholder="__(field.placeholder || field.label)"
|
:placeholder="getPlaceholder(field)"
|
||||||
input-class="border-none"
|
input-class="border-none"
|
||||||
/>
|
/>
|
||||||
<DatePicker
|
<DatePicker
|
||||||
v-else-if="field.type === 'Date'"
|
v-else-if="field.type === 'Date'"
|
||||||
v-model="data[field.name]"
|
v-model="data[field.name]"
|
||||||
:placeholder="__(field.placeholder || field.label)"
|
:placeholder="getPlaceholder(field)"
|
||||||
input-class="border-none"
|
input-class="border-none"
|
||||||
/>
|
/>
|
||||||
<FormControl
|
<FormControl
|
||||||
@ -196,19 +196,19 @@
|
|||||||
['Small Text', 'Text', 'Long Text'].includes(field.type)
|
['Small Text', 'Text', 'Long Text'].includes(field.type)
|
||||||
"
|
"
|
||||||
type="textarea"
|
type="textarea"
|
||||||
:placeholder="__(field.placeholder || field.label)"
|
:placeholder="getPlaceholder(field)"
|
||||||
v-model="data[field.name]"
|
v-model="data[field.name]"
|
||||||
/>
|
/>
|
||||||
<FormControl
|
<FormControl
|
||||||
v-else-if="['Int'].includes(field.type)"
|
v-else-if="['Int'].includes(field.type)"
|
||||||
type="number"
|
type="number"
|
||||||
:placeholder="__(field.placeholder || field.label)"
|
:placeholder="getPlaceholder(field)"
|
||||||
v-model="data[field.name]"
|
v-model="data[field.name]"
|
||||||
/>
|
/>
|
||||||
<FormControl
|
<FormControl
|
||||||
v-else
|
v-else
|
||||||
type="text"
|
type="text"
|
||||||
:placeholder="__(field.placeholder || field.label)"
|
:placeholder="getPlaceholder(field)"
|
||||||
v-model="data[field.name]"
|
v-model="data[field.name]"
|
||||||
:disabled="Boolean(field.read_only)"
|
:disabled="Boolean(field.read_only)"
|
||||||
/>
|
/>
|
||||||
@ -235,6 +235,17 @@ const props = defineProps({
|
|||||||
sections: Array,
|
sections: Array,
|
||||||
data: Object,
|
data: Object,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const getPlaceholder = (field) => {
|
||||||
|
if (field.placeholder) {
|
||||||
|
return __(field.placeholder)
|
||||||
|
}
|
||||||
|
if (['Select', 'Link'].includes(field.type)) {
|
||||||
|
return __('Select {0}', [__(field.label)])
|
||||||
|
} else {
|
||||||
|
return __('Enter {0}', [__(field.label)])
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@ -141,7 +141,7 @@ const props = defineProps({
|
|||||||
const show = defineModel()
|
const show = defineModel()
|
||||||
const tasks = defineModel('reloadTasks')
|
const tasks = defineModel('reloadTasks')
|
||||||
|
|
||||||
const emit = defineEmits(['updateTask'])
|
const emit = defineEmits(['updateTask', 'after'])
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const { getUser } = usersStore()
|
const { getUser } = usersStore()
|
||||||
@ -202,6 +202,7 @@ async function updateTask() {
|
|||||||
if (d.name) {
|
if (d.name) {
|
||||||
capture('task_created')
|
capture('task_created')
|
||||||
tasks.value.reload()
|
tasks.value.reload()
|
||||||
|
emit('after')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
show.value = false
|
show.value = false
|
||||||
|
|||||||
@ -147,10 +147,11 @@ function getRoute(notification) {
|
|||||||
dealId: notification.reference_name,
|
dealId: notification.reference_name,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: notification.route_name,
|
name: notification.route_name,
|
||||||
params: params,
|
params: params,
|
||||||
hash: '#' + notification.comment || notification.notification_type_doc,
|
hash: notification.hash,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -55,9 +55,9 @@
|
|||||||
v-else-if="field.type === 'select'"
|
v-else-if="field.type === 'select'"
|
||||||
class="form-control cursor-pointer [&_select]:cursor-pointer"
|
class="form-control cursor-pointer [&_select]:cursor-pointer"
|
||||||
type="select"
|
type="select"
|
||||||
:value="data[field.name]"
|
v-model="data[field.name]"
|
||||||
:options="field.options"
|
:options="field.options"
|
||||||
:debounce="500"
|
:placeholder="field.placeholder"
|
||||||
@change.stop="emit('update', field.name, $event.target.value)"
|
@change.stop="emit('update', field.name, $event.target.value)"
|
||||||
/>
|
/>
|
||||||
<Link
|
<Link
|
||||||
@ -141,7 +141,7 @@ const data = defineModel()
|
|||||||
const _fields = computed(() => {
|
const _fields = computed(() => {
|
||||||
let all_fields = []
|
let all_fields = []
|
||||||
props.fields?.forEach((field) => {
|
props.fields?.forEach((field) => {
|
||||||
let df = field.all_properties
|
let df = field?.all_properties
|
||||||
if (df?.depends_on) evaluate_depends_on(df.depends_on, field)
|
if (df?.depends_on) evaluate_depends_on(df.depends_on, field)
|
||||||
all_fields.push({
|
all_fields.push({
|
||||||
...field,
|
...field,
|
||||||
|
|||||||
@ -133,9 +133,9 @@ function saveChanges() {
|
|||||||
let _sections = JSON.parse(JSON.stringify(sections.data))
|
let _sections = JSON.parse(JSON.stringify(sections.data))
|
||||||
_sections.forEach((section) => {
|
_sections.forEach((section) => {
|
||||||
if (!section.fields) return
|
if (!section.fields) return
|
||||||
section.fields = section.fields.map(
|
section.fields = section.fields
|
||||||
(field) => field.fieldname || field.name,
|
.map((field) => field.name || field.fieldname)
|
||||||
)
|
.filter(Boolean)
|
||||||
})
|
})
|
||||||
loading.value = true
|
loading.value = true
|
||||||
call(
|
call(
|
||||||
|
|||||||
@ -3,16 +3,17 @@ import { useRoute, useRouter } from 'vue-router'
|
|||||||
import { useDebounceFn, useStorage } from '@vueuse/core'
|
import { useDebounceFn, useStorage } from '@vueuse/core'
|
||||||
|
|
||||||
export function useActiveTabManager(tabs, storageKey) {
|
export function useActiveTabManager(tabs, storageKey) {
|
||||||
const activieTab = useStorage(storageKey, 'activity')
|
const activeTab = useStorage(storageKey, 'activity')
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
const preserveLastVisitedTab = useDebounceFn((tabName) => {
|
const preserveLastVisitedTab = useDebounceFn((tabName) => {
|
||||||
activieTab.value = tabName.toLowerCase()
|
activeTab.value = tabName.toLowerCase()
|
||||||
}, 300)
|
}, 300)
|
||||||
|
|
||||||
function setActiveTabInUrl(tabName) {
|
function setActiveTabInUrl(tabName) {
|
||||||
let hash = '#' + tabName.toLowerCase()
|
let hash = '#' + tabName.toLowerCase()
|
||||||
|
if (route.hash === hash) return
|
||||||
router.push({ ...route, hash })
|
router.push({ ...route, hash })
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -21,7 +22,7 @@ export function useActiveTabManager(tabs, storageKey) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function findTabIndex(tabName) {
|
function findTabIndex(tabName) {
|
||||||
return tabs.value.findIndex(
|
return tabs.value?.findIndex(
|
||||||
(tabOptions) => tabOptions.name.toLowerCase() === tabName,
|
(tabOptions) => tabOptions.name.toLowerCase() === tabName,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -31,22 +32,18 @@ export function useActiveTabManager(tabs, storageKey) {
|
|||||||
return index !== -1 ? index : 0 // Default to the first tab if not found
|
return index !== -1 ? index : 0 // Default to the first tab if not found
|
||||||
}
|
}
|
||||||
|
|
||||||
function getActiveTabFromLocalStorage() {
|
|
||||||
return activieTab.value
|
|
||||||
}
|
|
||||||
|
|
||||||
function getActiveTab() {
|
function getActiveTab() {
|
||||||
let activeTab = getActiveTabFromUrl()
|
let _activeTab = getActiveTabFromUrl()
|
||||||
if (activeTab) {
|
if (_activeTab) {
|
||||||
let index = findTabIndex(activeTab)
|
let index = findTabIndex(_activeTab)
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
preserveLastVisitedTab(activeTab)
|
preserveLastVisitedTab(_activeTab)
|
||||||
return index
|
return index
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
let lastVisitedTab = getActiveTabFromLocalStorage()
|
let lastVisitedTab = activeTab.value
|
||||||
if (lastVisitedTab) {
|
if (lastVisitedTab) {
|
||||||
return getTabIndex(lastVisitedTab)
|
return getTabIndex(lastVisitedTab)
|
||||||
}
|
}
|
||||||
@ -57,7 +54,7 @@ export function useActiveTabManager(tabs, storageKey) {
|
|||||||
const tabIndex = ref(getActiveTab())
|
const tabIndex = ref(getActiveTab())
|
||||||
|
|
||||||
watch(tabIndex, (tabIndexValue) => {
|
watch(tabIndex, (tabIndexValue) => {
|
||||||
let currentTab = tabs.value[tabIndexValue].name
|
let currentTab = tabs.value?.[tabIndexValue].name
|
||||||
setActiveTabInUrl(currentTab)
|
setActiveTabInUrl(currentTab)
|
||||||
preserveLastVisitedTab(currentTab)
|
preserveLastVisitedTab(currentTab)
|
||||||
})
|
})
|
||||||
@ -71,11 +68,15 @@ export function useActiveTabManager(tabs, storageKey) {
|
|||||||
let index = findTabIndex(tabName)
|
let index = findTabIndex(tabName)
|
||||||
if (index === -1) index = 0
|
if (index === -1) index = 0
|
||||||
|
|
||||||
let currentTab = tabs.value[index].name
|
let currentTab = tabs.value?.[index].name
|
||||||
preserveLastVisitedTab(currentTab)
|
preserveLastVisitedTab(currentTab)
|
||||||
tabIndex.value = index
|
tabIndex.value = index
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
watch(tabs, () => {
|
||||||
|
tabIndex.value = getActiveTab()
|
||||||
|
})
|
||||||
|
|
||||||
return { tabIndex }
|
return { tabIndex }
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,7 +40,7 @@
|
|||||||
<Activities
|
<Activities
|
||||||
ref="activities"
|
ref="activities"
|
||||||
doctype="CRM Deal"
|
doctype="CRM Deal"
|
||||||
:title="tab.name"
|
:tabs="tabs"
|
||||||
v-model:reload="reload"
|
v-model:reload="reload"
|
||||||
v-model:tabIndex="tabIndex"
|
v-model:tabIndex="tabIndex"
|
||||||
v-model="deal"
|
v-model="deal"
|
||||||
@ -356,7 +356,6 @@ import { ref, computed, h, onMounted, onBeforeUnmount } from 'vue'
|
|||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import { useActiveTabManager } from '@/composables/useActiveTabManager'
|
import { useActiveTabManager } from '@/composables/useActiveTabManager'
|
||||||
|
|
||||||
|
|
||||||
const { $dialog, $socket, makeCall } = globalStore()
|
const { $dialog, $socket, makeCall } = globalStore()
|
||||||
const { statusOptions, getDealStatus } = statusesStore()
|
const { statusOptions, getDealStatus } = statusesStore()
|
||||||
const { isManager } = usersStore()
|
const { isManager } = usersStore()
|
||||||
|
|||||||
@ -45,7 +45,6 @@
|
|||||||
<Activities
|
<Activities
|
||||||
ref="activities"
|
ref="activities"
|
||||||
doctype="CRM Lead"
|
doctype="CRM Lead"
|
||||||
:title="tab.name"
|
|
||||||
:tabs="tabs"
|
:tabs="tabs"
|
||||||
v-model:reload="reload"
|
v-model:reload="reload"
|
||||||
v-model:tabIndex="tabIndex"
|
v-model:tabIndex="tabIndex"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user