Merge pull request #316 from shariquerik/posthog-telemetry
feat: Init posthog telemetry to analyse crm usage
This commit is contained in:
commit
73d9231bbe
@ -2,6 +2,7 @@ from bs4 import BeautifulSoup
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe.translate import get_all_translations
|
from frappe.translate import get_all_translations
|
||||||
from frappe.utils import cstr
|
from frappe.utils import cstr
|
||||||
|
from frappe.utils.telemetry import POSTHOG_HOST_FIELD, POSTHOG_PROJECT_FIELD
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist(allow_guest=True)
|
@frappe.whitelist(allow_guest=True)
|
||||||
@ -44,4 +45,13 @@ def get_user_signature():
|
|||||||
content = ""
|
content = ""
|
||||||
if (cstr(_signature) or signature):
|
if (cstr(_signature) or signature):
|
||||||
content = f'<br><p class="signature">{signature}</p>'
|
content = f'<br><p class="signature">{signature}</p>'
|
||||||
return content
|
return content
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def get_posthog_settings():
|
||||||
|
return {
|
||||||
|
"posthog_project_id": frappe.conf.get(POSTHOG_PROJECT_FIELD),
|
||||||
|
"posthog_host": frappe.conf.get(POSTHOG_HOST_FIELD),
|
||||||
|
"enable_telemetry": frappe.get_system_settings("enable_telemetry"),
|
||||||
|
"telemetry_site_age": frappe.utils.telemetry.site_age(),
|
||||||
|
}
|
||||||
@ -437,6 +437,7 @@ import { globalStore } from '@/stores/global'
|
|||||||
import { usersStore } from '@/stores/users'
|
import { usersStore } from '@/stores/users'
|
||||||
import { contactsStore } from '@/stores/contacts'
|
import { contactsStore } from '@/stores/contacts'
|
||||||
import { whatsappEnabled } from '@/composables/settings'
|
import { whatsappEnabled } from '@/composables/settings'
|
||||||
|
import { capture } from '@/telemetry'
|
||||||
import { Button, Tooltip, createResource } from 'frappe-ui'
|
import { Button, Tooltip, createResource } from 'frappe-ui'
|
||||||
import { useElementVisibility } from '@vueuse/core'
|
import { useElementVisibility } from '@vueuse/core'
|
||||||
import {
|
import {
|
||||||
@ -552,6 +553,7 @@ onMounted(() => {
|
|||||||
|
|
||||||
function sendTemplate(template) {
|
function sendTemplate(template) {
|
||||||
showWhatsappTemplates.value = false
|
showWhatsappTemplates.value = false
|
||||||
|
capture('send_whatsapp_template', { doctype: props.doctype })
|
||||||
createResource({
|
createResource({
|
||||||
url: 'crm.api.whatsapp.send_whatsapp_template',
|
url: 'crm.api.whatsapp.send_whatsapp_template',
|
||||||
params: {
|
params: {
|
||||||
|
|||||||
@ -170,9 +170,9 @@ import DoubleCheckIcon from '@/components/Icons/DoubleCheckIcon.vue'
|
|||||||
import DocumentIcon from '@/components/Icons/DocumentIcon.vue'
|
import DocumentIcon from '@/components/Icons/DocumentIcon.vue'
|
||||||
import ReactIcon from '@/components/Icons/ReactIcon.vue'
|
import ReactIcon from '@/components/Icons/ReactIcon.vue'
|
||||||
import { dateFormat } from '@/utils'
|
import { dateFormat } from '@/utils'
|
||||||
|
import { capture } from '@/telemetry'
|
||||||
import { Tooltip, Dropdown, createResource } from 'frappe-ui'
|
import { Tooltip, Dropdown, createResource } from 'frappe-ui'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import FeatherIcon from 'frappe-ui/src/components/FeatherIcon.vue'
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
messages: Array,
|
messages: Array,
|
||||||
@ -219,6 +219,7 @@ function reactOnMessage(name, emoji) {
|
|||||||
},
|
},
|
||||||
auto: true,
|
auto: true,
|
||||||
onSuccess() {
|
onSuccess() {
|
||||||
|
capture('whatsapp_react_on_message')
|
||||||
list.value.reload()
|
list.value.reload()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@ -39,6 +39,7 @@
|
|||||||
() => {
|
() => {
|
||||||
content += emoji
|
content += emoji
|
||||||
$refs.textarea.$el.focus()
|
$refs.textarea.$el.focus()
|
||||||
|
capture('whatsapp_emoji_added')
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
@ -65,8 +66,8 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import IconPicker from '@/components/IconPicker.vue'
|
import IconPicker from '@/components/IconPicker.vue'
|
||||||
import SmileIcon from '@/components/Icons/SmileIcon.vue'
|
import SmileIcon from '@/components/Icons/SmileIcon.vue'
|
||||||
|
import { capture } from '@/telemetry'
|
||||||
import { createResource, Textarea, FileUploader, Dropdown } from 'frappe-ui'
|
import { createResource, Textarea, FileUploader, Dropdown } from 'frappe-ui'
|
||||||
import FeatherIcon from 'frappe-ui/src/components/FeatherIcon.vue'
|
|
||||||
import { ref, nextTick, watch } from 'vue'
|
import { ref, nextTick, watch } from 'vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@ -92,6 +93,7 @@ function uploadFile(file) {
|
|||||||
whatsapp.value.attach = file.file_url
|
whatsapp.value.attach = file.file_url
|
||||||
whatsapp.value.content_type = fileType.value
|
whatsapp.value.content_type = fileType.value
|
||||||
sendWhatsAppMessage()
|
sendWhatsAppMessage()
|
||||||
|
capture('whatsapp_upload_file')
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendTextMessage(event) {
|
function sendTextMessage(event) {
|
||||||
@ -99,6 +101,7 @@ function sendTextMessage(event) {
|
|||||||
sendWhatsAppMessage()
|
sendWhatsAppMessage()
|
||||||
textarea.value.$el.blur()
|
textarea.value.$el.blur()
|
||||||
content.value = ''
|
content.value = ''
|
||||||
|
capture('whatsapp_send_message')
|
||||||
}
|
}
|
||||||
|
|
||||||
async function sendWhatsAppMessage() {
|
async function sendWhatsAppMessage() {
|
||||||
|
|||||||
@ -39,6 +39,8 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import AppsIcon from '@/components/Icons/AppsIcon.vue'
|
import AppsIcon from '@/components/Icons/AppsIcon.vue'
|
||||||
import { Popover, createResource } from 'frappe-ui'
|
import { Popover, createResource } from 'frappe-ui'
|
||||||
|
import { onUnmounted } from 'vue';
|
||||||
|
import { stopRecording } from '@/telemetry';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
active: Boolean,
|
active: Boolean,
|
||||||
@ -70,4 +72,8 @@ const apps = createResource({
|
|||||||
return _apps
|
return _apps
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
stopRecording()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -197,6 +197,7 @@ import { Device } from '@twilio/voice-sdk'
|
|||||||
import { useDraggable, useWindowSize } from '@vueuse/core'
|
import { useDraggable, useWindowSize } from '@vueuse/core'
|
||||||
import { globalStore } from '@/stores/global'
|
import { globalStore } from '@/stores/global'
|
||||||
import { contactsStore } from '@/stores/contacts'
|
import { contactsStore } from '@/stores/contacts'
|
||||||
|
import { capture } from '@/telemetry'
|
||||||
import { Avatar, call } from 'frappe-ui'
|
import { Avatar, call } from 'frappe-ui'
|
||||||
import { onMounted, ref, watch } from 'vue'
|
import { onMounted, ref, watch } from 'vue'
|
||||||
|
|
||||||
@ -403,6 +404,8 @@ async function makeOutgoingCall(number) {
|
|||||||
showCallPopup.value = true
|
showCallPopup.value = true
|
||||||
callStatus.value = 'initiating'
|
callStatus.value = 'initiating'
|
||||||
|
|
||||||
|
capture('make_outgoing_call')
|
||||||
|
|
||||||
_call.on('messageReceived', (message) => {
|
_call.on('messageReceived', (message) => {
|
||||||
let info = message.content
|
let info = message.content
|
||||||
callStatus.value = info.CallStatus
|
callStatus.value = info.CallStatus
|
||||||
|
|||||||
@ -92,6 +92,7 @@ import AttachmentIcon from '@/components/Icons/AttachmentIcon.vue'
|
|||||||
import AttachmentItem from '@/components/AttachmentItem.vue'
|
import AttachmentItem from '@/components/AttachmentItem.vue'
|
||||||
import { usersStore } from '@/stores/users'
|
import { usersStore } from '@/stores/users'
|
||||||
import { TextEditorBubbleMenu, TextEditor, FileUploader } from 'frappe-ui'
|
import { TextEditorBubbleMenu, TextEditor, FileUploader } from 'frappe-ui'
|
||||||
|
import { capture } from '@/telemetry'
|
||||||
import { EditorContent } from '@tiptap/vue-3'
|
import { EditorContent } from '@tiptap/vue-3'
|
||||||
import { ref, computed, defineModel } from 'vue'
|
import { ref, computed, defineModel } from 'vue'
|
||||||
|
|
||||||
@ -139,6 +140,7 @@ function appendEmoji() {
|
|||||||
editor.value.commands.insertContent(emoji.value)
|
editor.value.commands.insertContent(emoji.value)
|
||||||
editor.value.commands.focus()
|
editor.value.commands.focus()
|
||||||
emoji.value = ''
|
emoji.value = ''
|
||||||
|
capture('emoji_inserted_in_comment', { emoji: emoji.value })
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeAttachment(attachment) {
|
function removeAttachment(attachment) {
|
||||||
|
|||||||
@ -88,6 +88,7 @@ import EmailEditor from '@/components/EmailEditor.vue'
|
|||||||
import CommentBox from '@/components/CommentBox.vue'
|
import CommentBox from '@/components/CommentBox.vue'
|
||||||
import CommentIcon from '@/components/Icons/CommentIcon.vue'
|
import CommentIcon from '@/components/Icons/CommentIcon.vue'
|
||||||
import Email2Icon from '@/components/Icons/Email2Icon.vue'
|
import Email2Icon from '@/components/Icons/Email2Icon.vue'
|
||||||
|
import { capture } from '@/telemetry'
|
||||||
import { usersStore } from '@/stores/users'
|
import { usersStore } from '@/stores/users'
|
||||||
import { useStorage } from '@vueuse/core'
|
import { useStorage } from '@vueuse/core'
|
||||||
import { call, createResource } from 'frappe-ui'
|
import { call, createResource } from 'frappe-ui'
|
||||||
@ -176,6 +177,10 @@ async function sendMail() {
|
|||||||
let subject = newEmailEditor.value.subject
|
let subject = newEmailEditor.value.subject
|
||||||
let cc = newEmailEditor.value.ccEmails || []
|
let cc = newEmailEditor.value.ccEmails || []
|
||||||
let bcc = newEmailEditor.value.bccEmails || []
|
let bcc = newEmailEditor.value.bccEmails || []
|
||||||
|
|
||||||
|
if (attachments.value.length) {
|
||||||
|
capture('email_attachments_added')
|
||||||
|
}
|
||||||
await call('frappe.core.doctype.communication.email.make', {
|
await call('frappe.core.doctype.communication.email.make', {
|
||||||
recipients: recipients.join(', '),
|
recipients: recipients.join(', '),
|
||||||
attachments: attachments.value.map((x) => x.name),
|
attachments: attachments.value.map((x) => x.name),
|
||||||
@ -200,6 +205,7 @@ async function sendComment() {
|
|||||||
comment_by: getUser()?.full_name || undefined,
|
comment_by: getUser()?.full_name || undefined,
|
||||||
})
|
})
|
||||||
if (comment && attachments.value.length) {
|
if (comment && attachments.value.length) {
|
||||||
|
capture('comment_attachments_added')
|
||||||
await call('crm.api.comment.add_attachments', {
|
await call('crm.api.comment.add_attachments', {
|
||||||
name: comment.name,
|
name: comment.name,
|
||||||
attachments: attachments.value.map((x) => x.name),
|
attachments: attachments.value.map((x) => x.name),
|
||||||
@ -214,6 +220,7 @@ async function submitEmail() {
|
|||||||
newEmail.value = ''
|
newEmail.value = ''
|
||||||
reload.value = true
|
reload.value = true
|
||||||
emit('scroll')
|
emit('scroll')
|
||||||
|
capture('email_sent', { doctype: props.doctype })
|
||||||
}
|
}
|
||||||
|
|
||||||
async function submitComment() {
|
async function submitComment() {
|
||||||
@ -223,6 +230,7 @@ async function submitComment() {
|
|||||||
newComment.value = ''
|
newComment.value = ''
|
||||||
reload.value = true
|
reload.value = true
|
||||||
emit('scroll')
|
emit('scroll')
|
||||||
|
capture('comment_sent', { doctype: props.doctype })
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleEmailBox() {
|
function toggleEmailBox() {
|
||||||
|
|||||||
@ -175,6 +175,7 @@ import AttachmentItem from '@/components/AttachmentItem.vue'
|
|||||||
import MultiselectInput from '@/components/Controls/MultiselectInput.vue'
|
import MultiselectInput from '@/components/Controls/MultiselectInput.vue'
|
||||||
import EmailTemplateSelectorModal from '@/components/Modals/EmailTemplateSelectorModal.vue'
|
import EmailTemplateSelectorModal from '@/components/Modals/EmailTemplateSelectorModal.vue'
|
||||||
import { TextEditorBubbleMenu, TextEditor, FileUploader, call } from 'frappe-ui'
|
import { TextEditorBubbleMenu, TextEditor, FileUploader, call } from 'frappe-ui'
|
||||||
|
import { capture } from '@/telemetry'
|
||||||
import { validateEmail } from '@/utils'
|
import { validateEmail } from '@/utils'
|
||||||
import Paragraph from '@tiptap/extension-paragraph'
|
import Paragraph from '@tiptap/extension-paragraph'
|
||||||
import { EditorContent } from '@tiptap/vue-3'
|
import { EditorContent } from '@tiptap/vue-3'
|
||||||
@ -273,12 +274,14 @@ async function applyEmailTemplate(template) {
|
|||||||
editor.value.commands.setContent(data.message)
|
editor.value.commands.setContent(data.message)
|
||||||
}
|
}
|
||||||
showEmailTemplateSelectorModal.value = false
|
showEmailTemplateSelectorModal.value = false
|
||||||
|
capture('email_template_applied', { doctype: props.doctype })
|
||||||
}
|
}
|
||||||
|
|
||||||
function appendEmoji() {
|
function appendEmoji() {
|
||||||
editor.value.commands.insertContent(emoji.value)
|
editor.value.commands.insertContent(emoji.value)
|
||||||
editor.value.commands.focus()
|
editor.value.commands.focus()
|
||||||
emoji.value = ''
|
emoji.value = ''
|
||||||
|
capture('emoji_inserted_in_email', { emoji: emoji.value })
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleCC() {
|
function toggleCC() {
|
||||||
|
|||||||
@ -21,6 +21,7 @@ import EditValueModal from '@/components/Modals/EditValueModal.vue'
|
|||||||
import AssignmentModal from '@/components/Modals/AssignmentModal.vue'
|
import AssignmentModal from '@/components/Modals/AssignmentModal.vue'
|
||||||
import { setupListActions, createToast } from '@/utils'
|
import { setupListActions, createToast } from '@/utils'
|
||||||
import { globalStore } from '@/stores/global'
|
import { globalStore } from '@/stores/global'
|
||||||
|
import { capture } from '@/telemetry'
|
||||||
import { call } from 'frappe-ui'
|
import { call } from 'frappe-ui'
|
||||||
import { ref, onMounted } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
@ -69,6 +70,7 @@ function convertToDeal(selections, unselectAll) {
|
|||||||
label: __('Convert'),
|
label: __('Convert'),
|
||||||
variant: 'solid',
|
variant: 'solid',
|
||||||
onClick: (close) => {
|
onClick: (close) => {
|
||||||
|
capture('bulk_convert_to_deal')
|
||||||
Array.from(selections).forEach((name) => {
|
Array.from(selections).forEach((name) => {
|
||||||
call('crm.fcrm.doctype.crm_lead.crm_lead.convert_to_deal', {
|
call('crm.fcrm.doctype.crm_lead.crm_lead.convert_to_deal', {
|
||||||
lead: name,
|
lead: name,
|
||||||
@ -103,6 +105,7 @@ function deleteValues(selections, unselectAll) {
|
|||||||
variant: 'solid',
|
variant: 'solid',
|
||||||
theme: 'red',
|
theme: 'red',
|
||||||
onClick: (close) => {
|
onClick: (close) => {
|
||||||
|
capture('bulk_delete')
|
||||||
call('frappe.desk.reportview.delete_items', {
|
call('frappe.desk.reportview.delete_items', {
|
||||||
items: JSON.stringify(Array.from(selections)),
|
items: JSON.stringify(Array.from(selections)),
|
||||||
doctype: props.doctype,
|
doctype: props.doctype,
|
||||||
@ -145,6 +148,7 @@ function clearAssignemnts(selections, unselectAll) {
|
|||||||
variant: 'solid',
|
variant: 'solid',
|
||||||
theme: 'red',
|
theme: 'red',
|
||||||
onClick: (close) => {
|
onClick: (close) => {
|
||||||
|
capture('bulk_clear_assignment')
|
||||||
call('frappe.desk.form.assign_to.remove_multiple', {
|
call('frappe.desk.form.assign_to.remove_multiple', {
|
||||||
doctype: props.doctype,
|
doctype: props.doctype,
|
||||||
names: JSON.stringify(Array.from(selections)),
|
names: JSON.stringify(Array.from(selections)),
|
||||||
|
|||||||
@ -80,6 +80,7 @@
|
|||||||
import UserAvatar from '@/components/UserAvatar.vue'
|
import UserAvatar from '@/components/UserAvatar.vue'
|
||||||
import Link from '@/components/Controls/Link.vue'
|
import Link from '@/components/Controls/Link.vue'
|
||||||
import { usersStore } from '@/stores/users'
|
import { usersStore } from '@/stores/users'
|
||||||
|
import { capture } from '@/telemetry'
|
||||||
import { Tooltip, call } from 'frappe-ui'
|
import { Tooltip, call } from 'frappe-ui'
|
||||||
import { ref, computed, onMounted } from 'vue'
|
import { ref, computed, onMounted } from 'vue'
|
||||||
|
|
||||||
@ -161,6 +162,7 @@ function updateAssignees() {
|
|||||||
|
|
||||||
if (addedAssignees.length) {
|
if (addedAssignees.length) {
|
||||||
if (props.docs.size) {
|
if (props.docs.size) {
|
||||||
|
capture('bulk_assign_to', { doctype: props.doctype })
|
||||||
call('frappe.desk.form.assign_to.add_multiple', {
|
call('frappe.desk.form.assign_to.add_multiple', {
|
||||||
doctype: props.doctype,
|
doctype: props.doctype,
|
||||||
name: JSON.stringify(Array.from(props.docs)),
|
name: JSON.stringify(Array.from(props.docs)),
|
||||||
@ -171,6 +173,7 @@ function updateAssignees() {
|
|||||||
emit('reload')
|
emit('reload')
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
capture('assign_to', { doctype: props.doctype })
|
||||||
call('frappe.desk.form.assign_to.add', {
|
call('frappe.desk.form.assign_to.add', {
|
||||||
doctype: props.doctype,
|
doctype: props.doctype,
|
||||||
name: props.doc.name,
|
name: props.doc.name,
|
||||||
|
|||||||
@ -92,6 +92,7 @@ import CertificateIcon from '@/components/Icons/CertificateIcon.vue'
|
|||||||
import EditIcon from '@/components/Icons/EditIcon.vue'
|
import EditIcon from '@/components/Icons/EditIcon.vue'
|
||||||
import Dropdown from '@/components/frappe-ui/Dropdown.vue'
|
import Dropdown from '@/components/frappe-ui/Dropdown.vue'
|
||||||
import { usersStore } from '@/stores/users'
|
import { usersStore } from '@/stores/users'
|
||||||
|
import { capture } from '@/telemetry'
|
||||||
import { call, createResource } from 'frappe-ui'
|
import { call, createResource } from 'frappe-ui'
|
||||||
import { ref, nextTick, watch, computed } from 'vue'
|
import { ref, nextTick, watch, computed } from 'vue'
|
||||||
import { createToast } from '@/utils'
|
import { createToast } from '@/utils'
|
||||||
@ -160,7 +161,10 @@ async function callInsertDoc() {
|
|||||||
..._contact.value,
|
..._contact.value,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
doc.name && handleContactUpdate(doc)
|
if (doc.name) {
|
||||||
|
capture('contact_created')
|
||||||
|
handleContactUpdate(doc)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleContactUpdate(doc) {
|
function handleContactUpdate(doc) {
|
||||||
|
|||||||
@ -61,6 +61,7 @@ import EditIcon from '@/components/Icons/EditIcon.vue'
|
|||||||
import Fields from '@/components/Fields.vue'
|
import Fields from '@/components/Fields.vue'
|
||||||
import { usersStore } from '@/stores/users'
|
import { usersStore } from '@/stores/users'
|
||||||
import { statusesStore } from '@/stores/statuses'
|
import { statusesStore } from '@/stores/statuses'
|
||||||
|
import { capture } from '@/telemetry'
|
||||||
import { Switch, createResource } from 'frappe-ui'
|
import { Switch, createResource } from 'frappe-ui'
|
||||||
import { computed, ref, reactive, onMounted, nextTick } from 'vue'
|
import { computed, ref, reactive, onMounted, nextTick } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
@ -201,6 +202,7 @@ function createDeal() {
|
|||||||
isDealCreating.value = true
|
isDealCreating.value = true
|
||||||
},
|
},
|
||||||
onSuccess(name) {
|
onSuccess(name) {
|
||||||
|
capture('deal_created')
|
||||||
isDealCreating.value = false
|
isDealCreating.value = false
|
||||||
show.value = false
|
show.value = false
|
||||||
router.push({ name: 'Deal', params: { dealId: name } })
|
router.push({ name: 'Deal', params: { dealId: name } })
|
||||||
|
|||||||
@ -36,6 +36,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import Link from '@/components/Controls/Link.vue'
|
import Link from '@/components/Controls/Link.vue'
|
||||||
import Autocomplete from '@/components/frappe-ui/Autocomplete.vue'
|
import Autocomplete from '@/components/frappe-ui/Autocomplete.vue'
|
||||||
|
import { capture } from '@/telemetry'
|
||||||
import { FormControl, call, createResource, TextEditor, DatePicker } from 'frappe-ui'
|
import { FormControl, call, createResource, TextEditor, DatePicker } from 'frappe-ui'
|
||||||
import { ref, computed, onMounted, h } from 'vue'
|
import { ref, computed, onMounted, h } from 'vue'
|
||||||
|
|
||||||
@ -115,6 +116,7 @@ function updateValues() {
|
|||||||
newValue.value = ''
|
newValue.value = ''
|
||||||
loading.value = false
|
loading.value = false
|
||||||
show.value = false
|
show.value = false
|
||||||
|
capture('bulk_update', { doctype: props.doctype })
|
||||||
emit('reload')
|
emit('reload')
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -96,6 +96,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { capture } from '@/telemetry'
|
||||||
import { Checkbox, Select, TextEditor, call } from 'frappe-ui'
|
import { Checkbox, Select, TextEditor, call } from 'frappe-ui'
|
||||||
import { ref, nextTick, watch } from 'vue'
|
import { ref, nextTick, watch } from 'vue'
|
||||||
|
|
||||||
@ -171,7 +172,10 @@ async function callInsertDoc() {
|
|||||||
..._emailTemplate.value,
|
..._emailTemplate.value,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
doc.name && handleEmailTemplateUpdate(doc)
|
if (doc.name) {
|
||||||
|
capture('email_template_created', { doctype: doc.reference_doctype })
|
||||||
|
handleEmailTemplateUpdate(doc)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleEmailTemplateUpdate(doc) {
|
function handleEmailTemplateUpdate(doc) {
|
||||||
|
|||||||
@ -46,6 +46,7 @@ import EditIcon from '@/components/Icons/EditIcon.vue'
|
|||||||
import Fields from '@/components/Fields.vue'
|
import Fields from '@/components/Fields.vue'
|
||||||
import { usersStore } from '@/stores/users'
|
import { usersStore } from '@/stores/users'
|
||||||
import { statusesStore } from '@/stores/statuses'
|
import { statusesStore } from '@/stores/statuses'
|
||||||
|
import { capture } from '@/telemetry'
|
||||||
import { createResource } from 'frappe-ui'
|
import { createResource } from 'frappe-ui'
|
||||||
import { computed, onMounted, ref, reactive, nextTick } from 'vue'
|
import { computed, onMounted, ref, reactive, nextTick } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
@ -153,6 +154,7 @@ function createNewLead() {
|
|||||||
isLeadCreating.value = true
|
isLeadCreating.value = true
|
||||||
},
|
},
|
||||||
onSuccess(data) {
|
onSuccess(data) {
|
||||||
|
capture('lead_created')
|
||||||
isLeadCreating.value = false
|
isLeadCreating.value = false
|
||||||
show.value = false
|
show.value = false
|
||||||
router.push({ name: 'Lead', params: { leadId: data.name } })
|
router.push({ name: 'Lead', params: { leadId: data.name } })
|
||||||
|
|||||||
@ -66,6 +66,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import ArrowUpRightIcon from '@/components/Icons/ArrowUpRightIcon.vue'
|
import ArrowUpRightIcon from '@/components/Icons/ArrowUpRightIcon.vue'
|
||||||
|
import { capture } from '@/telemetry'
|
||||||
import { TextEditor, call } from 'frappe-ui'
|
import { TextEditor, call } from 'frappe-ui'
|
||||||
import { ref, nextTick, watch } from 'vue'
|
import { ref, nextTick, watch } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
@ -124,6 +125,7 @@ async function updateNote() {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
if (d.name) {
|
if (d.name) {
|
||||||
|
capture('note_created')
|
||||||
notes.value?.reload()
|
notes.value?.reload()
|
||||||
emit('after', d, true)
|
emit('after', d, true)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -67,6 +67,7 @@ import OrganizationsIcon from '@/components/Icons/OrganizationsIcon.vue'
|
|||||||
import TerritoryIcon from '@/components/Icons/TerritoryIcon.vue'
|
import TerritoryIcon from '@/components/Icons/TerritoryIcon.vue'
|
||||||
import { usersStore } from '@/stores/users'
|
import { usersStore } from '@/stores/users'
|
||||||
import { formatNumberIntoCurrency } from '@/utils'
|
import { formatNumberIntoCurrency } from '@/utils'
|
||||||
|
import { capture } from '@/telemetry'
|
||||||
import { call, FeatherIcon, createResource } from 'frappe-ui'
|
import { call, FeatherIcon, createResource } from 'frappe-ui'
|
||||||
import { ref, nextTick, watch, computed, h } from 'vue'
|
import { ref, nextTick, watch, computed, h } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
@ -157,7 +158,10 @@ async function callInsertDoc() {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
loading.value = false
|
loading.value = false
|
||||||
doc.name && handleOrganizationUpdate(doc)
|
if (doc.name) {
|
||||||
|
capture('organization_created')
|
||||||
|
handleOrganizationUpdate(doc)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleOrganizationUpdate(doc, renamed = false) {
|
function handleOrganizationUpdate(doc, renamed = false) {
|
||||||
|
|||||||
@ -118,6 +118,7 @@ import UserAvatar from '@/components/UserAvatar.vue'
|
|||||||
import Link from '@/components/Controls/Link.vue'
|
import Link from '@/components/Controls/Link.vue'
|
||||||
import { taskStatusOptions, taskPriorityOptions } from '@/utils'
|
import { taskStatusOptions, taskPriorityOptions } from '@/utils'
|
||||||
import { usersStore } from '@/stores/users'
|
import { usersStore } from '@/stores/users'
|
||||||
|
import { capture } from '@/telemetry'
|
||||||
import { TextEditor, Dropdown, Tooltip, call, DateTimePicker } from 'frappe-ui'
|
import { TextEditor, Dropdown, Tooltip, call, DateTimePicker } from 'frappe-ui'
|
||||||
import { ref, watch, nextTick, onMounted } from 'vue'
|
import { ref, watch, nextTick, onMounted } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
@ -199,6 +200,7 @@ async function updateTask() {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
if (d.name) {
|
if (d.name) {
|
||||||
|
capture('task_created')
|
||||||
tasks.value.reload()
|
tasks.value.reload()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -75,7 +75,6 @@ const templates = createListResource({
|
|||||||
filters: { status: 'APPROVED', for_doctype: ['in', [props.doctype, '']] },
|
filters: { status: 'APPROVED', for_doctype: ['in', [props.doctype, '']] },
|
||||||
orderBy: 'modified desc',
|
orderBy: 'modified desc',
|
||||||
pageLength: 99999,
|
pageLength: 99999,
|
||||||
auto: true,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
|||||||
@ -18,10 +18,7 @@
|
|||||||
<div class="flex gap-1">
|
<div class="flex gap-1">
|
||||||
<Tooltip :text="__('Mark all as read')">
|
<Tooltip :text="__('Mark all as read')">
|
||||||
<div>
|
<div>
|
||||||
<Button
|
<Button variant="ghost" @click="() => markAllAsRead()">
|
||||||
variant="ghost"
|
|
||||||
@click="() => notificationsStore().mark_as_read.reload()"
|
|
||||||
>
|
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<MarkAsDoneIcon class="h-4 w-4" />
|
<MarkAsDoneIcon class="h-4 w-4" />
|
||||||
</template>
|
</template>
|
||||||
@ -48,7 +45,7 @@
|
|||||||
:key="n.comment"
|
:key="n.comment"
|
||||||
:to="getRoute(n)"
|
:to="getRoute(n)"
|
||||||
class="flex cursor-pointer items-start gap-2.5 px-4 py-2.5 hover:bg-gray-100"
|
class="flex cursor-pointer items-start gap-2.5 px-4 py-2.5 hover:bg-gray-100"
|
||||||
@click="mark_as_read(n.comment || n.notification_type_doc)"
|
@click="markAsRead(n.comment || n.notification_type_doc)"
|
||||||
>
|
>
|
||||||
<div class="mt-1 flex items-center gap-2.5">
|
<div class="mt-1 flex items-center gap-2.5">
|
||||||
<div
|
<div
|
||||||
@ -98,6 +95,7 @@ import { notificationsStore } from '@/stores/notifications'
|
|||||||
import { globalStore } from '@/stores/global'
|
import { globalStore } from '@/stores/global'
|
||||||
import { timeAgo } from '@/utils'
|
import { timeAgo } from '@/utils'
|
||||||
import { onClickOutside } from '@vueuse/core'
|
import { onClickOutside } from '@vueuse/core'
|
||||||
|
import { capture } from '@/telemetry'
|
||||||
import { Tooltip } from 'frappe-ui'
|
import { Tooltip } from 'frappe-ui'
|
||||||
import { ref, onMounted, onBeforeUnmount } from 'vue'
|
import { ref, onMounted, onBeforeUnmount } from 'vue'
|
||||||
|
|
||||||
@ -113,17 +111,23 @@ onClickOutside(
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
ignore: ['#notifications-btn'],
|
ignore: ['#notifications-btn'],
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
function toggleNotificationPanel() {
|
function toggleNotificationPanel() {
|
||||||
notificationsStore().toggle()
|
notificationsStore().toggle()
|
||||||
}
|
}
|
||||||
|
|
||||||
function mark_as_read(doc) {
|
function markAsRead(doc) {
|
||||||
|
capture('notification_mark_as_read')
|
||||||
notificationsStore().mark_doc_as_read(doc)
|
notificationsStore().mark_doc_as_read(doc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function markAllAsRead() {
|
||||||
|
capture('notification_mark_all_as_read')
|
||||||
|
notificationsStore().mark_as_read.reload()
|
||||||
|
}
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
$socket.off('crm_notification')
|
$socket.off('crm_notification')
|
||||||
})
|
})
|
||||||
|
|||||||
@ -5,7 +5,9 @@
|
|||||||
:key="s.label"
|
:key="s.label"
|
||||||
class="flex items-center gap-2 text-base leading-5"
|
class="flex items-center gap-2 text-base leading-5"
|
||||||
>
|
>
|
||||||
<div class="sm:w-[106px] w-36 text-sm text-gray-600">{{ __(s.label) }}</div>
|
<div class="sm:w-[106px] w-36 text-sm text-gray-600">
|
||||||
|
{{ __(s.label) }}
|
||||||
|
</div>
|
||||||
<div class="grid min-h-[28px] items-center">
|
<div class="grid min-h-[28px] items-center">
|
||||||
<Tooltip v-if="s.tooltipText" :text="__(s.tooltipText)">
|
<Tooltip v-if="s.tooltipText" :text="__(s.tooltipText)">
|
||||||
<div class="ml-2 cursor-pointer">
|
<div class="ml-2 cursor-pointer">
|
||||||
@ -43,6 +45,7 @@
|
|||||||
import { Dropdown, Tooltip } from 'frappe-ui'
|
import { Dropdown, Tooltip } from 'frappe-ui'
|
||||||
import { timeAgo, dateFormat, formatTime, dateTooltipFormat } from '@/utils'
|
import { timeAgo, dateFormat, formatTime, dateTooltipFormat } from '@/utils'
|
||||||
import { statusesStore } from '@/stores/statuses'
|
import { statusesStore } from '@/stores/statuses'
|
||||||
|
import { capture } from '@/telemetry'
|
||||||
import { computed, defineModel } from 'vue'
|
import { computed, defineModel } from 'vue'
|
||||||
|
|
||||||
const data = defineModel()
|
const data = defineModel()
|
||||||
@ -58,8 +61,8 @@ let slaSection = computed(() => {
|
|||||||
data.value.sla_status == 'Failed'
|
data.value.sla_status == 'Failed'
|
||||||
? 'red'
|
? 'red'
|
||||||
: data.value.sla_status == 'Fulfilled'
|
: data.value.sla_status == 'Fulfilled'
|
||||||
? 'green'
|
? 'green'
|
||||||
: 'orange'
|
: 'orange'
|
||||||
|
|
||||||
if (status == 'First Response Due') {
|
if (status == 'First Response Due') {
|
||||||
status = timeAgo(data.value.response_by)
|
status = timeAgo(data.value.response_by)
|
||||||
@ -94,11 +97,13 @@ let slaSection = computed(() => {
|
|||||||
options: communicationStatuses.data?.map((status) => ({
|
options: communicationStatuses.data?.map((status) => ({
|
||||||
label: status.name,
|
label: status.name,
|
||||||
value: status.name,
|
value: status.name,
|
||||||
onClick: () =>
|
onClick: () => {
|
||||||
emit('updateField', 'communication_status', status.name),
|
capture('sla_status_change')
|
||||||
|
emit('updateField', 'communication_status', status.name)
|
||||||
|
},
|
||||||
})),
|
})),
|
||||||
},
|
},
|
||||||
]
|
],
|
||||||
)
|
)
|
||||||
return sections
|
return sections
|
||||||
})
|
})
|
||||||
|
|||||||
@ -56,6 +56,7 @@
|
|||||||
import Fields from '@/components/Fields.vue'
|
import Fields from '@/components/Fields.vue'
|
||||||
import QuickEntryLayoutBuilder from '@/components/Settings/QuickEntryLayoutBuilder.vue'
|
import QuickEntryLayoutBuilder from '@/components/Settings/QuickEntryLayoutBuilder.vue'
|
||||||
import { useDebounceFn } from '@vueuse/core'
|
import { useDebounceFn } from '@vueuse/core'
|
||||||
|
import { capture } from '@/telemetry'
|
||||||
import { Dialog, Badge, Switch, call, createResource } from 'frappe-ui'
|
import { Dialog, Badge, Switch, call, createResource } from 'frappe-ui'
|
||||||
import { ref, watch, onMounted, nextTick } from 'vue'
|
import { ref, watch, onMounted, nextTick } from 'vue'
|
||||||
|
|
||||||
@ -122,6 +123,7 @@ function saveChanges() {
|
|||||||
).then(() => {
|
).then(() => {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
show.value = false
|
show.value = false
|
||||||
|
capture('quick_entry_layout_builder', { doctype: _doctype.value })
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -74,6 +74,7 @@ import Section from '@/components/Section.vue'
|
|||||||
import SectionFields from '@/components/SectionFields.vue'
|
import SectionFields from '@/components/SectionFields.vue'
|
||||||
import SidePanelLayoutBuilder from '@/components/Settings/SidePanelLayoutBuilder.vue'
|
import SidePanelLayoutBuilder from '@/components/Settings/SidePanelLayoutBuilder.vue'
|
||||||
import { useDebounceFn } from '@vueuse/core'
|
import { useDebounceFn } from '@vueuse/core'
|
||||||
|
import { capture } from '@/telemetry'
|
||||||
import { Dialog, Badge, Switch, call, createResource } from 'frappe-ui'
|
import { Dialog, Badge, Switch, call, createResource } from 'frappe-ui'
|
||||||
import { ref, watch, onMounted, nextTick } from 'vue'
|
import { ref, watch, onMounted, nextTick } from 'vue'
|
||||||
|
|
||||||
@ -143,6 +144,7 @@ function saveChanges() {
|
|||||||
).then(() => {
|
).then(() => {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
show.value = false
|
show.value = false
|
||||||
|
capture('side_panel_layout_builder', { doctype: _doctype.value })
|
||||||
emit('reload')
|
emit('reload')
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,5 @@
|
|||||||
import './index.css'
|
import './index.css'
|
||||||
|
|
||||||
import { createApp } from 'vue'
|
|
||||||
import router from './router'
|
|
||||||
import App from './App.vue'
|
|
||||||
import { createPinia } from 'pinia'
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
FrappeUI,
|
FrappeUI,
|
||||||
Button,
|
Button,
|
||||||
@ -19,9 +14,14 @@ import {
|
|||||||
frappeRequest,
|
frappeRequest,
|
||||||
FeatherIcon,
|
FeatherIcon,
|
||||||
} from 'frappe-ui'
|
} from 'frappe-ui'
|
||||||
import translationPlugin from './translation'
|
import { createApp } from 'vue'
|
||||||
|
import { createPinia } from 'pinia'
|
||||||
import { createDialog } from './utils/dialogs'
|
import { createDialog } from './utils/dialogs'
|
||||||
import { initSocket } from './socket'
|
import { initSocket } from './socket'
|
||||||
|
import router from './router'
|
||||||
|
import translationPlugin from './translation'
|
||||||
|
import { posthogPlugin } from './telemetry'
|
||||||
|
import App from './App.vue'
|
||||||
|
|
||||||
let globalComponents = {
|
let globalComponents = {
|
||||||
Button,
|
Button,
|
||||||
@ -45,6 +45,7 @@ app.use(FrappeUI)
|
|||||||
app.use(pinia)
|
app.use(pinia)
|
||||||
app.use(router)
|
app.use(router)
|
||||||
app.use(translationPlugin)
|
app.use(translationPlugin)
|
||||||
|
app.use(posthogPlugin)
|
||||||
for (let key in globalComponents) {
|
for (let key in globalComponents) {
|
||||||
app.component(key, globalComponents[key])
|
app.component(key, globalComponents[key])
|
||||||
}
|
}
|
||||||
@ -61,7 +62,7 @@ if (import.meta.env.DEV) {
|
|||||||
socket = initSocket()
|
socket = initSocket()
|
||||||
app.config.globalProperties.$socket = socket
|
app.config.globalProperties.$socket = socket
|
||||||
app.mount('#app')
|
app.mount('#app')
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
socket = initSocket()
|
socket = initSocket()
|
||||||
|
|||||||
@ -312,6 +312,7 @@ import { organizationsStore } from '@/stores/organizations'
|
|||||||
import { statusesStore } from '@/stores/statuses'
|
import { statusesStore } from '@/stores/statuses'
|
||||||
import { usersStore } from '@/stores/users'
|
import { usersStore } from '@/stores/users'
|
||||||
import { whatsappEnabled, callEnabled } from '@/composables/settings'
|
import { whatsappEnabled, callEnabled } from '@/composables/settings'
|
||||||
|
import { capture } from '@/telemetry'
|
||||||
import {
|
import {
|
||||||
createResource,
|
createResource,
|
||||||
FileUploader,
|
FileUploader,
|
||||||
@ -587,6 +588,7 @@ async function convertToDeal(updated) {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
if (deal) {
|
if (deal) {
|
||||||
|
capture('convert_lead_to_deal')
|
||||||
if (updated) {
|
if (updated) {
|
||||||
await organizations.reload()
|
await organizations.reload()
|
||||||
await contacts.reload()
|
await contacts.reload()
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
|
import IndicatorIcon from '@/components/Icons/IndicatorIcon.vue'
|
||||||
|
import { capture } from '@/telemetry'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { createListResource } from 'frappe-ui'
|
import { createListResource } from 'frappe-ui'
|
||||||
import { reactive, h } from 'vue'
|
import { reactive, h } from 'vue'
|
||||||
import IndicatorIcon from '@/components/Icons/IndicatorIcon.vue'
|
|
||||||
|
|
||||||
export const statusesStore = defineStore('crm-statuses', () => {
|
export const statusesStore = defineStore('crm-statuses', () => {
|
||||||
let leadStatusesByName = reactive({})
|
let leadStatusesByName = reactive({})
|
||||||
@ -103,6 +104,7 @@ export const statusesStore = defineStore('crm-statuses', () => {
|
|||||||
class: statusesByName[status].iconColorClass,
|
class: statusesByName[status].iconColorClass,
|
||||||
}),
|
}),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
|
capture('status_changed', { doctype, status })
|
||||||
action && action('status', statusesByName[status].name)
|
action && action('status', statusesByName[status].name)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
97
frontend/src/telemetry.ts
Normal file
97
frontend/src/telemetry.ts
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
import '../../../frappe/frappe/public/js/lib/posthog.js'
|
||||||
|
import { createResource } from 'frappe-ui'
|
||||||
|
import { computed } from 'vue'
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
posthog: any
|
||||||
|
}
|
||||||
|
}
|
||||||
|
type PosthogSettings = {
|
||||||
|
posthog_project_id: string
|
||||||
|
posthog_host: string
|
||||||
|
enable_telemetry: boolean
|
||||||
|
telemetry_site_age: number
|
||||||
|
}
|
||||||
|
interface CaptureOptions {
|
||||||
|
data: {
|
||||||
|
user: string
|
||||||
|
[key: string]: string | number | boolean | object
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let posthog: typeof window.posthog = window.posthog
|
||||||
|
|
||||||
|
// Posthog Settings
|
||||||
|
let posthogSettings = createResource({
|
||||||
|
url: 'crm.api.get_posthog_settings',
|
||||||
|
cache: 'posthog_settings',
|
||||||
|
onSuccess: (ps: PosthogSettings) => initPosthog(ps),
|
||||||
|
})
|
||||||
|
|
||||||
|
let isTelemetryEnabled = () => {
|
||||||
|
if (!posthogSettings.data) return false
|
||||||
|
|
||||||
|
return (
|
||||||
|
posthogSettings.data.enable_telemetry &&
|
||||||
|
posthogSettings.data.posthog_project_id &&
|
||||||
|
posthogSettings.data.posthog_host
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Posthog Initialization
|
||||||
|
function initPosthog(ps: PosthogSettings) {
|
||||||
|
if (!isTelemetryEnabled()) return
|
||||||
|
|
||||||
|
posthog.init(ps.posthog_project_id, {
|
||||||
|
api_host: ps.posthog_host,
|
||||||
|
person_profiles: 'identified_only',
|
||||||
|
autocapture: false,
|
||||||
|
capture_pageview: true,
|
||||||
|
capture_pageleave: true,
|
||||||
|
enable_heatmaps: false,
|
||||||
|
disable_session_recording: false,
|
||||||
|
loaded: (ph: typeof posthog) => {
|
||||||
|
window.posthog = ph
|
||||||
|
ph.identify(window.location.hostname)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Posthog Functions
|
||||||
|
function capture(
|
||||||
|
event: string,
|
||||||
|
options: CaptureOptions = { data: { user: '' } },
|
||||||
|
) {
|
||||||
|
if (!isTelemetryEnabled()) return
|
||||||
|
window.posthog.capture(`crm_${event}`, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
function startRecording() {
|
||||||
|
if (!isTelemetryEnabled()) return
|
||||||
|
if (window.posthog?.__loaded) {
|
||||||
|
window.posthog.startSessionRecording()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopRecording() {
|
||||||
|
if (!isTelemetryEnabled()) return
|
||||||
|
if (window.posthog?.__loaded && window.posthog.sessionRecordingStarted()) {
|
||||||
|
window.posthog.stopSessionRecording()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Posthog Plugin
|
||||||
|
function posthogPlugin(app: any) {
|
||||||
|
app.config.globalProperties.posthog = posthog
|
||||||
|
if (!window.posthog?.length) posthogSettings.fetch()
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
posthog,
|
||||||
|
posthogSettings,
|
||||||
|
posthogPlugin,
|
||||||
|
capture,
|
||||||
|
startRecording,
|
||||||
|
stopRecording,
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user