diff --git a/crm/crm/doctype/crm_lead/api.py b/crm/crm/doctype/crm_lead/api.py index 9e77709f..740a6e11 100644 --- a/crm/crm/doctype/crm_lead/api.py +++ b/crm/crm/doctype/crm_lead/api.py @@ -39,6 +39,8 @@ def get_activities(doc, docinfo): for version in docinfo.versions: data = json.loads(version.data) + if not data.get("changed"): + continue if change := data.get("changed")[0]: activity_type = "changed" field_label = next((f.label for f in lead_fields_meta if f.fieldname == change[0]), None) diff --git a/frontend/src/pages/Deal.vue b/frontend/src/pages/Deal.vue index d2435e4e..318a9390 100644 --- a/frontend/src/pages/Deal.vue +++ b/frontend/src/pages/Deal.vue @@ -19,9 +19,14 @@ - + - + - updateDeal()" /> @@ -80,10 +84,11 @@ > - {{ deal.data.lead_name }} + {{ deal.data.organization_name }} - + + + @@ -218,6 +229,24 @@ + + + { let items = [{ label: 'Deals', route: { name: 'Deals' } }] items.push({ - label: deal.data.lead_name, + label: deal.data.organization_name, route: { name: 'Deal', params: { dealId: deal.data.name } }, }) return items @@ -345,11 +381,11 @@ const tabs = computed(() => { ), activityTitle: 'Calls', }, - { - label: 'Tasks', - icon: TaskIcon, - activityTitle: 'Tasks', - }, + // { + // label: 'Tasks', + // icon: TaskIcon, + // activityTitle: 'Tasks', + // }, { label: 'Notes', icon: NoteIcon, @@ -376,20 +412,9 @@ function onTabChange(index) { const detailSections = computed(() => { return [ { - label: 'About this deal', + label: 'Organization', opened: true, fields: [ - { - label: 'Status', - type: 'select', - name: 'deal_status', - options: statusDropdownOptions(deal.data, 'deal'), - }, - { - label: 'Deal owner', - type: 'link', - name: 'lead_owner', - }, { label: 'Organization', type: 'data', @@ -400,10 +425,30 @@ const detailSections = computed(() => { type: 'data', name: 'website', }, + { + label: 'Amount', + type: 'number', + name: 'annual_revenue', + }, + { + label: 'Close date', + type: 'date', + name: 'close_date', + }, + { + label: 'Probability', + type: 'data', + name: 'probability', + }, + { + label: 'Next step', + type: 'data', + name: 'next_step', + } ], }, { - label: 'Person', + label: 'Contacts', opened: true, fields: [ { @@ -423,7 +468,7 @@ const detailSections = computed(() => { }, { label: 'Mobile no.', - type: 'phone', + type: 'tel', name: 'mobile_no', }, ], diff --git a/frontend/src/pages/Deals.vue b/frontend/src/pages/Deals.vue index caf977fc..65a7e682 100644 --- a/frontend/src/pages/Deals.vue +++ b/frontend/src/pages/Deals.vue @@ -102,11 +102,9 @@ const leads = createListResource({ doctype: 'CRM Lead', fields: [ 'name', - 'first_name', - 'lead_name', - 'image', 'organization_name', 'organization_logo', + 'annual_revenue', 'deal_status', 'email', 'mobile_no', @@ -140,23 +138,23 @@ watch( ) const columns = [ - { - label: 'Name', - key: 'lead_name', - type: 'avatar', - size: 'w-44', - }, { label: 'Organization', key: 'organization_name', type: 'logo', - size: 'w-44', + size: 'w-48', + }, + { + label: 'Amount', + key: 'annual_revenue', + type: 'data', + size: 'w-24', }, { label: 'Status', key: 'deal_status', type: 'indicator', - size: 'w-44', + size: 'w-36', }, { label: 'Email', @@ -168,13 +166,19 @@ const columns = [ label: 'Mobile no', key: 'mobile_no', type: 'phone', - size: 'w-44', + size: 'w-32', }, { label: 'Lead owner', key: 'lead_owner', type: 'avatar', - size: 'w-44', + size: 'w-36', + }, + { + label: 'Last modified', + key: 'modified', + type: 'pretty_date', + size: 'w-28', }, ] @@ -182,15 +186,11 @@ const rows = computed(() => { return leads.data?.map((lead) => { return { name: lead.name, - lead_name: { - label: lead.lead_name, - image: lead.image, - image_label: lead.first_name, - }, organization_name: { label: lead.organization_name, logo: lead.organization_logo, }, + annual_revenue: lead.annual_revenue, deal_status: { label: lead.deal_status, color: dealStatuses[lead.deal_status]?.color, @@ -198,6 +198,7 @@ const rows = computed(() => { email: lead.email, mobile_no: lead.mobile_no, lead_owner: lead.lead_owner && getUser(lead.lead_owner), + modified: lead.modified, } }) }) diff --git a/frontend/src/pages/Lead.vue b/frontend/src/pages/Lead.vue index 062fa2fc..efff336c 100644 --- a/frontend/src/pages/Lead.vue +++ b/frontend/src/pages/Lead.vue @@ -19,7 +19,10 @@ - + @@ -31,8 +34,8 @@ - - updateLead()" /> + + @@ -96,7 +99,13 @@ - + + + @@ -158,6 +167,14 @@ /> field.change(e)" + :placeholder="field.placeholder" + class="form-control" + /> + { ), activityTitle: 'Calls', }, - { - label: 'Tasks', - icon: TaskIcon, - activityTitle: 'Tasks', - }, + // { + // label: 'Tasks', + // icon: TaskIcon, + // activityTitle: 'Tasks', + // }, { label: 'Notes', icon: NoteIcon, @@ -379,17 +405,6 @@ const detailSections = computed(() => { label: 'About this lead', opened: true, fields: [ - { - label: 'Status', - type: 'select', - name: 'status', - options: statusDropdownOptions(lead.data), - }, - { - label: 'Lead owner', - type: 'link', - name: 'lead_owner', - }, { label: 'Organization', type: 'data', @@ -400,12 +415,66 @@ const detailSections = computed(() => { type: 'data', name: 'website', }, + { + label: 'Job title', + type: 'data', + name: 'job_title', + }, + { + label: 'Source', + type: 'link', + name: 'source', + placeholder: 'Select source...', + options: [ + { label: 'Advertisement', value: 'Advertisement' }, + { label: 'Web', value: 'Web' }, + { label: 'Others', value: 'Others' }, + ], + change: (data) => { + lead.data.source = data.value + }, + }, + { + label: 'Industry', + type: 'link', + name: 'industry', + placeholder: 'Select industry...', + options: [ + { label: 'Advertising', value: 'Advertising' }, + { label: 'Agriculture', value: 'Agriculture' }, + { label: 'Banking', value: 'Banking' }, + { label: 'Others', value: 'Others' }, + ], + change: (data) => { + lead.data.industry = data.value + }, + }, ], }, { label: 'Person', opened: true, fields: [ + { + label: 'Salutation', + type: 'link', + name: 'salutation', + placeholder: 'Mr./Mrs./Ms.', + options: [ + { label: 'Dr', value: 'Dr' }, + { label: 'Mr', value: 'Mr' }, + { label: 'Mrs', value: 'Mrs' }, + { label: 'Ms', value: 'Ms' }, + { label: 'Mx', value: 'Mx' }, + { label: 'Prof', value: 'Prof' }, + { label: 'Master', value: 'Master' }, + { label: 'Madam', value: 'Madam' }, + { label: 'Miss', value: 'Miss' }, + ], + change: (data) => { + lead.data.salutation = data.value + }, + }, { label: 'First name', type: 'data', @@ -444,6 +513,13 @@ const activeAgents = computed(() => { } }) }) + +function convertToDeal() { + lead.data.status = 'Qualified' + lead.data.is_deal = 1 + updateLead() + router.push({ name: 'Deal', params: { dealId: lead.data.name } }) +} diff --git a/frontend/src/pages/Leads.vue b/frontend/src/pages/Leads.vue index 16fed559..75896818 100644 --- a/frontend/src/pages/Leads.vue +++ b/frontend/src/pages/Leads.vue @@ -96,6 +96,10 @@ function getFilter() { } } +function getSortBy() { + return getOrderBy() || 'modified desc' +} + const leads = createListResource({ type: 'list', doctype: 'CRM Lead', @@ -113,7 +117,7 @@ const leads = createListResource({ 'modified', ], filters: getFilter(), - orderBy: 'modified desc', + orderBy: getSortBy(), pageLength: 20, auto: true, }) @@ -122,7 +126,7 @@ watch( () => getOrderBy(), (value, old_value) => { if (!value && !old_value) return - leads.orderBy = getOrderBy() || 'modified desc' + leads.orderBy = getSortBy() leads.reload() }, { immediate: true } @@ -143,37 +147,43 @@ const columns = [ label: 'Name', key: 'lead_name', type: 'avatar', - size: 'w-44', + size: 'w-48', }, { label: 'Organization', key: 'organization_name', type: 'logo', - size: 'w-44', + size: 'w-40', }, { label: 'Status', key: 'status', type: 'indicator', - size: 'w-44', + size: 'w-36', }, { label: 'Email', key: 'email', type: 'email', - size: 'w-44', + size: 'w-40', }, { label: 'Mobile no', key: 'mobile_no', type: 'phone', - size: 'w-44', + size: 'w-32', }, { label: 'Lead owner', key: 'lead_owner', type: 'avatar', - size: 'w-44', + size: 'w-36', + }, + { + label: 'Last modified', + key: 'modified', + type: 'pretty_date', + size: 'w-28', }, ] @@ -197,6 +207,7 @@ const rows = computed(() => { email: lead.email, mobile_no: lead.mobile_no, lead_owner: lead.lead_owner && getUser(lead.lead_owner), + modified: lead.modified, } }) }) diff --git a/frontend/src/utils.js b/frontend/src/utils.js index a4721319..3e93f980 100644 --- a/frontend/src/utils.js +++ b/frontend/src/utils.js @@ -14,25 +14,58 @@ export function timeAgo(date) { export const dateTooltipFormat = 'ddd, MMM D, YYYY h:mm A' export const leadStatuses = { - Open: { label: 'Open', color: '!text-gray-600' }, - Contacted: { label: 'Contacted', color: '!text-orange-600' }, - Nurture: { label: 'Nurture', color: '!text-blue-600' }, - Qualified: { label: 'Qualified', color: '!text-green-600' }, - Unqualified: { label: 'Unqualified', color: '!text-red-600' }, - Junk: { label: 'Junk', color: '!text-purple-600' }, + Open: { label: 'Open', color: '!text-gray-600', bgColor: '!bg-gray-200' }, + Contacted: { + label: 'Contacted', + color: '!text-orange-600', + bgColor: '!bg-orange-200', + }, + Nurture: { + label: 'Nurture', + color: '!text-blue-600', + bgColor: '!bg-blue-200', + }, + Qualified: { + label: 'Qualified', + color: '!text-green-600', + bgColor: '!bg-green-200', + }, + Unqualified: { + label: 'Unqualified', + color: '!text-red-600', + bgColor: '!bg-red-200', + }, + Junk: { label: 'Junk', color: '!text-purple-600', bgColor: '!bg-purple-200' }, } export const dealStatuses = { - Qualification: { label: 'Qualification', color: '!text-gray-600' }, - 'Demo/Making': { label: 'Demo/Making', color: '!text-orange-600' }, + Qualification: { + label: 'Qualification', + color: '!text-gray-600', + bgColor: '!bg-gray-200', + }, + 'Demo/Making': { + label: 'Demo/Making', + color: '!text-orange-600', + bgColor: '!bg-orange-200', + }, 'Proposal/Quotation': { label: 'Proposal/Quotation', color: '!text-blue-600', + bgColor: '!bg-blue-200', }, - Negotiation: { label: 'Negotiation', color: '!text-yellow-600' }, - 'Ready to Close': { label: 'Ready to Close', color: '!text-purple-600' }, - Won: { label: 'Won', color: '!text-green-600' }, - Lost: { label: 'Lost', color: '!text-red-600' }, + Negotiation: { + label: 'Negotiation', + color: '!text-yellow-600', + bgColor: '!bg-yellow-100', + }, + 'Ready to Close': { + label: 'Ready to Close', + color: '!text-purple-600', + bgColor: '!bg-purple-200', + }, + Won: { label: 'Won', color: '!text-green-600', bgColor: '!bg-green-200' }, + Lost: { label: 'Lost', color: '!text-red-600', bgColor: '!bg-red-200' }, } export function statusDropdownOptions(data, doctype) { @@ -56,3 +89,7 @@ export function statusDropdownOptions(data, doctype) { } return options } + +export function openWebsite(url) { + window.open(url, '_blank') +}