diff --git a/crm/api/onboarding.py b/crm/api/onboarding.py new file mode 100644 index 00000000..1ac09477 --- /dev/null +++ b/crm/api/onboarding.py @@ -0,0 +1,19 @@ +import json + +import frappe + + +@frappe.whitelist() +def update_user_onboarding_status(steps: str): + steps = json.loads(steps) + + # get the current onboarding status + onboarding_status = frappe.db.get_value("User", frappe.session.user, "onboarding_status") + onboarding_status = frappe.parse_json(onboarding_status) + + # update the onboarding status + onboarding_status["frappe_crm_onboarding_status"] = steps + + frappe.db.set_value( + "User", frappe.session.user, "onboarding_status", json.dumps(onboarding_status), update_modified=False + ) diff --git a/frontend/src/components/Modals/LeadModal.vue b/frontend/src/components/Modals/LeadModal.vue index b7e4f99e..aa158658 100644 --- a/frontend/src/components/Modals/LeadModal.vue +++ b/frontend/src/components/Modals/LeadModal.vue @@ -47,6 +47,7 @@ import FieldLayout from '@/components/FieldLayout/FieldLayout.vue' import { usersStore } from '@/stores/users' import { statusesStore } from '@/stores/statuses' import { isMobileView } from '@/composables/settings' +import { useOnboarding } from '@/composables/onboarding' import { capture } from '@/telemetry' import { createResource } from 'frappe-ui' import { computed, onMounted, ref, reactive, nextTick } from 'vue' @@ -58,6 +59,7 @@ const props = defineProps({ const { getUser, isManager } = usersStore() const { getLeadStatus, statusOptions } = statusesStore() +const { updateOnboardingStep } = useOnboarding() const show = defineModel() const router = useRouter() @@ -166,6 +168,7 @@ function createNewLead() { isLeadCreating.value = false show.value = false router.push({ name: 'Lead', params: { leadId: data.name } }) + updateOnboardingStep('create_first_lead') }, onError(err) { isLeadCreating.value = false diff --git a/frontend/src/composables/onboarding.js b/frontend/src/composables/onboarding.js index a8ff7ea3..d188e2c2 100644 --- a/frontend/src/composables/onboarding.js +++ b/frontend/src/composables/onboarding.js @@ -6,6 +6,9 @@ import CommentIcon from '@/components/Icons/CommentIcon.vue' import EmailIcon from '@/components/Icons/EmailIcon.vue' import TaskIcon from '@/components/Icons/TaskIcon.vue' import StepsIcon from '@/components/Icons/StepsIcon.vue' +import { capture } from '@/telemetry' +import { showSettings, activeSettingsPage } from '@/composables/settings' +import { call } from 'frappe-ui' import { useRouter } from 'vue-router' import { ref, reactive, computed, markRaw } from 'vue' @@ -31,6 +34,13 @@ const steps = reactive([ title: 'Invite your team', icon: markRaw(InviteIcon), completed: false, + onClick: () => { + if (steps[1].completed) return + minimize.value = true + + showSettings.value = true + activeSettingsPage.value = 'Invite Members' + }, }, { name: 'convert_lead_to_deal', @@ -93,6 +103,35 @@ export function useOnboarding() { return false } + function updateOnboardingStep(step) { + if (stepsCompleted.value) return + let user = window.user + if (!user) return false + + if (!user.onboarding_status['frappe_crm_onboarding_status']) { + user.onboarding_status['frappe_crm_onboarding_status'] = steps.map( + (s) => { + return { name: s.name, completed: false } + }, + ) + } + + let _steps = user.onboarding_status['frappe_crm_onboarding_status'] + let index = _steps.findIndex((s) => s.name === step) + if (index !== -1) { + _steps[index].completed = true + steps[index].completed = true + } + + window.user = user + + capture('onboarding_' + step) + + call('crm.api.onboarding.update_user_onboarding_status', { + steps: JSON.stringify(_steps), + }) + } + return { minimize, steps, @@ -100,5 +139,6 @@ export function useOnboarding() { totalSteps, completedPercentage, checkOnboardingStatus, + updateOnboardingStep, } }