diff --git a/frontend/src/components/Icons/ExternalLinkIcon.vue b/frontend/src/components/Icons/ExternalLinkIcon.vue new file mode 100644 index 00000000..0597fed3 --- /dev/null +++ b/frontend/src/components/Icons/ExternalLinkIcon.vue @@ -0,0 +1,16 @@ + diff --git a/frontend/src/pages/Contact.vue b/frontend/src/pages/Contact.vue index 38cda47e..a69b3b89 100644 --- a/frontend/src/pages/Contact.vue +++ b/frontend/src/pages/Contact.vue @@ -91,10 +91,10 @@ :key="field.name" class="flex items-center gap-2 px-3 text-base leading-5 last:mb-3" > -
+
{{ field.label }}
-
+
+
@@ -197,6 +202,7 @@ import PhoneIcon from '@/components/Icons/PhoneIcon.vue' import CameraIcon from '@/components/Icons/CameraIcon.vue' import LeadsIcon from '@/components/Icons/LeadsIcon.vue' import DealsIcon from '@/components/Icons/DealsIcon.vue' +import ExternalLinkIcon from '@/components/Icons/ExternalLinkIcon.vue' import LeadsListView from '@/components/ListViews/LeadsListView.vue' import DealsListView from '@/components/ListViews/DealsListView.vue' import { @@ -210,10 +216,12 @@ import { } from '@/utils' import { usersStore } from '@/stores/users.js' import { contactsStore } from '@/stores/contacts.js' +import { organizationsStore } from '@/stores/organizations.js' import { ref, computed, h } from 'vue' const { getContactByName, contacts } = contactsStore() const { getUser } = usersStore() +const { getOrganization, organizationOptions } = organizationsStore() const showContactModal = ref(false) @@ -297,8 +305,7 @@ const leads = createListResource({ 'first_name', 'lead_name', 'image', - 'organization_name', - 'organization_logo', + 'organization', 'status', 'email', 'mobile_no', @@ -320,8 +327,7 @@ const deals = createListResource({ cache: ['deals', props.contactId], fields: [ 'name', - 'organization_name', - 'organization_logo', + 'organization', 'annual_revenue', 'deal_status', 'email', @@ -361,9 +367,9 @@ function getLeadRowObject(lead) { image: lead.image, image_label: lead.first_name, }, - organization_name: { - label: lead.organization_name, - logo: lead.organization_logo, + organization: { + label: lead.organization, + logo: getOrganization(lead.organization)?.organization_logo, }, status: { label: lead.status, @@ -385,9 +391,9 @@ function getLeadRowObject(lead) { function getDealRowObject(deal) { return { name: deal.name, - organization_name: { - label: deal.organization_name, - logo: deal.organization_logo, + organization: { + label: deal.organization, + logo: getOrganization(deal.organization)?.organization_logo, }, annual_revenue: formatNumberIntoCurrency(deal.annual_revenue), deal_status: { @@ -415,7 +421,7 @@ const leadColumns = [ }, { label: 'Organization', - key: 'organization_name', + key: 'organization', width: '10rem', }, { @@ -448,7 +454,7 @@ const leadColumns = [ const dealColumns = [ { label: 'Organization', - key: 'organization_name', + key: 'organization', width: '11rem', }, { @@ -483,54 +489,68 @@ const dealColumns = [ }, ] -const details = [ - { - 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) => { - contact.value.salutation = data.value - updateContact('salutation', data.value) +const details = computed(() => { + return [ + { + 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) => { + contact.value.salutation = data.value + updateContact('salutation', data.value) + }, }, - }, - { - label: 'First name', - type: 'data', - name: 'first_name', - }, - { - label: 'Last name', - type: 'data', - name: 'last_name', - }, - { - label: 'Email', - type: 'email', - name: 'email', - }, - { - label: 'Mobile no.', - type: 'phone', - name: 'mobile_no', - }, - { - label: 'Organization', - type: 'data', - name: 'company_name', - }, -] + { + label: 'First name', + type: 'data', + name: 'first_name', + }, + { + label: 'Last name', + type: 'data', + name: 'last_name', + }, + { + label: 'Email', + type: 'email', + name: 'email', + }, + { + label: 'Mobile no.', + type: 'phone', + name: 'mobile_no', + }, + { + label: 'Organization', + type: 'link', + name: 'company_name', + placeholder: 'Select organization', + options: organizationOptions, + change: (data) => { + contact.value.company_name = data.value + updateContact('company_name', data.value) + }, + link: (data) => { + router.push({ + name: 'Organization', + params: { organizationId: data.value }, + }) + }, + }, + ] +}) function updateContact(fieldname, value) { createResource({ @@ -570,7 +590,18 @@ function updateContact(fieldname, value) { background: white; } +:deep(.form-control button) { + gap: 0; +} + +:deep(.form-control button > div) { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + :deep(.form-control button svg) { color: white; + width: 0; } diff --git a/frontend/src/pages/Deal.vue b/frontend/src/pages/Deal.vue index d9616bc9..b4e2f44c 100644 --- a/frontend/src/pages/Deal.vue +++ b/frontend/src/pages/Deal.vue @@ -4,7 +4,8 @@ - + - + +
+ {{ field.value }} +
+ @@ -311,8 +294,8 @@ import PhoneIcon from '@/components/Icons/PhoneIcon.vue' import TaskIcon from '@/components/Icons/TaskIcon.vue' import NoteIcon from '@/components/Icons/NoteIcon.vue' import IndicatorIcon from '@/components/Icons/IndicatorIcon.vue' -import CameraIcon from '@/components/Icons/CameraIcon.vue' import LinkIcon from '@/components/Icons/LinkIcon.vue' +import ExternalLinkIcon from '@/components/Icons/ExternalLinkIcon.vue' import LayoutHeader from '@/components/LayoutHeader.vue' import Toggler from '@/components/Toggler.vue' import Activities from '@/components/Activities.vue' @@ -326,12 +309,11 @@ import { } from '@/utils' import { usersStore } from '@/stores/users' import { contactsStore } from '@/stores/contacts' +import { organizationsStore } from '@/stores/organizations' import { createResource, FeatherIcon, - FileUploader, ErrorMessage, - Autocomplete, FormControl, Dropdown, Tooltip, @@ -340,9 +322,12 @@ import { Breadcrumbs, } from 'frappe-ui' import { ref, computed } from 'vue' +import { useRouter } from 'vue-router' -const { getUser, users } = usersStore() +const { getUser } = usersStore() const { contacts } = contactsStore() +const { getOrganization, organizationOptions } = organizationsStore() +const router = useRouter() const props = defineProps({ dealId: { @@ -394,7 +379,7 @@ function updateDeal(fieldname, value) { const breadcrumbs = computed(() => { let items = [{ label: 'Deals', route: { name: 'Deals' } }] items.push({ - label: deal.data.organization_name, + label: organization.value.name, route: { name: 'Deal', params: { dealId: deal.data.name } }, }) return items @@ -424,18 +409,6 @@ const tabs = [ }, ] -function changeDealImage(file) { - deal.data.organization_logo = file.file_url - updateDeal('organization_logo', file.file_url) -} - -function validateFile(file) { - let extn = file.name.split('.').pop().toLowerCase() - if (!['png', 'jpg', 'jpeg'].includes(extn)) { - return 'Only PNG and JPG images are allowed' - } -} - const detailSections = computed(() => { return [ { @@ -444,13 +417,26 @@ const detailSections = computed(() => { fields: [ { label: 'Organization', - type: 'data', - name: 'organization_name', + type: 'link', + name: 'organization', + placeholder: 'Select organization', + options: organizationOptions, + change: (data) => { + deal.data.organization = data.value + updateDeal('organization', data.value) + }, + link: () => { + router.push({ + name: 'Organization', + params: { organizationId: organization.value.name }, + }) + }, }, { label: 'Website', - type: 'data', + type: 'read_only', name: 'website', + value: organization.value?.website, }, { label: 'Amount', @@ -524,6 +510,10 @@ const detailSections = computed(() => { ] }) +const organization = computed(() => { + return getOrganization(deal.data.organization) +}) + function updateAssignedAgent(email) { deal.data.lead_owner = email updateDeal('lead_owner', email) @@ -538,7 +528,18 @@ function updateAssignedAgent(email) { background: white; } +:deep(.form-control button) { + gap: 0; +} + +:deep(.form-control button > div) { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + :deep(.form-control button svg) { color: white; + width: 0; } diff --git a/frontend/src/pages/Lead.vue b/frontend/src/pages/Lead.vue index 6ade3c4b..ff27bb16 100644 --- a/frontend/src/pages/Lead.vue +++ b/frontend/src/pages/Lead.vue @@ -4,7 +4,8 @@ - + - + + {{ field.value }} + + @@ -283,7 +297,9 @@ import PhoneIcon from '@/components/Icons/PhoneIcon.vue' import TaskIcon from '@/components/Icons/TaskIcon.vue' import NoteIcon from '@/components/Icons/NoteIcon.vue' import IndicatorIcon from '@/components/Icons/IndicatorIcon.vue' +import CameraIcon from '@/components/Icons/CameraIcon.vue' import LinkIcon from '@/components/Icons/LinkIcon.vue' +import ExternalLinkIcon from '@/components/Icons/ExternalLinkIcon.vue' import LayoutHeader from '@/components/LayoutHeader.vue' import Toggler from '@/components/Toggler.vue' import Activities from '@/components/Activities.vue' @@ -297,6 +313,7 @@ import { } from '@/utils' import { usersStore } from '@/stores/users' import { contactsStore } from '@/stores/contacts' +import { organizationsStore } from '@/stores/organizations' import { createResource, FileUploader, @@ -312,10 +329,10 @@ import { } from 'frappe-ui' import { ref, computed } from 'vue' import { useRouter } from 'vue-router' -import CameraIcon from '../components/Icons/CameraIcon.vue' -const { getUser, users } = usersStore() +const { getUser } = usersStore() const { contacts } = contactsStore() +const { getOrganization, organizationOptions } = organizationsStore() const router = useRouter() const props = defineProps({ @@ -421,13 +438,26 @@ const detailSections = computed(() => { fields: [ { label: 'Organization', - type: 'data', - name: 'organization_name', + type: 'link', + name: 'organization', + placeholder: 'Select organization', + options: organizationOptions, + change: (data) => { + lead.data.organization = data.value + updateLead('organization', data.value) + }, + link: () => { + router.push({ + name: 'Organization', + params: { organizationId: organization.value?.name }, + }) + }, }, { label: 'Website', - type: 'data', + type: 'read_only', name: 'website', + value: organization.value?.website, }, { label: 'Job title', @@ -517,6 +547,10 @@ const detailSections = computed(() => { ] }) +const organization = computed(() => { + return getOrganization(lead.data.organization) +}) + function convertToDeal() { lead.data.status = 'Qualified' lead.data.is_deal = 1 @@ -537,7 +571,18 @@ function updateAssignedAgent(email) { background: white; } +:deep(.form-control button) { + gap: 0; +} + +:deep(.form-control button > div) { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + :deep(.form-control button svg) { color: white; + width: 0; }