diff --git a/frontend/src/components/ListViews/OrganizationsListView.vue b/frontend/src/components/ListViews/OrganizationsListView.vue new file mode 100644 index 00000000..82e824e2 --- /dev/null +++ b/frontend/src/components/ListViews/OrganizationsListView.vue @@ -0,0 +1,71 @@ + + + + + + + + + + + + + {{ item.timeAgo }} + + + + + + + + diff --git a/frontend/src/pages/Organization.vue b/frontend/src/pages/Organization.vue index 2ab6f0d4..7d2de91d 100644 --- a/frontend/src/pages/Organization.vue +++ b/frontend/src/pages/Organization.vue @@ -1,11 +1,16 @@ - - - - + + + + + + + + + - - - - - - {{ organization.name }} - - - - - {{ website(organization.website) }} - - - · - - - - {{ organization.industry }} - - - · - - - - {{ organization.annual_revenue }} - - - - - - - - - - - - - - + + + - - + + import { + Breadcrumbs, FeatherIcon, Avatar, FileUploader, @@ -190,14 +226,13 @@ import { call, createListResource, } from 'frappe-ui' +import LayoutHeader from '@/components/LayoutHeader.vue' import OrganizationModal from '@/components/Modals/OrganizationModal.vue' import LeadsListView from '@/components/ListViews/LeadsListView.vue' import DealsListView from '@/components/ListViews/DealsListView.vue' import ContactsListView from '@/components/ListViews/ContactsListView.vue' import WebsiteIcon from '@/components/Icons/WebsiteIcon.vue' -import EmailIcon from '@/components/Icons/EmailIcon.vue' import EditIcon from '@/components/Icons/EditIcon.vue' -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' @@ -212,18 +247,32 @@ import { formatNumberIntoCurrency, } from '@/utils' import { usersStore } from '@/stores/users' -import { h, computed, ref, watch, onMounted } from 'vue' +import { h, computed, ref } from 'vue' const props = defineProps({ - organization: { - type: Object, + organizationId: { + type: String, required: true, }, }) -const { organizations } = organizationsStore() +const { organizations, getOrganization } = organizationsStore() const showOrganizationModal = ref(false) +const organization = computed(() => getOrganization(props.organizationId)) + +const breadcrumbs = computed(() => { + let items = [{ label: 'Organizations', route: { name: 'Organizations' } }] + items.push({ + label: props.organizationId, + route: { + name: 'Organization', + params: { organizationId: props.organizationId }, + }, + }) + return items +}) + function validateFile(file) { let extn = file.name.split('.').pop().toLowerCase() if (!['png', 'jpg', 'jpeg'].includes(extn)) { @@ -234,7 +283,7 @@ function validateFile(file) { async function changeOrganizationImage(file) { await call('frappe.client.set_value', { doctype: 'CRM Organization', - name: props.organization.name, + name: props.organizationId, fieldname: 'organization_logo', value: file?.file_url || '', }) @@ -253,7 +302,7 @@ async function deleteOrganization() { async onClick({ close }) { await call('frappe.client.delete', { doctype: 'CRM Organization', - name: props.organization.name, + name: props.organizationId, }) organizations.reload() close() @@ -291,7 +340,7 @@ const { getUser } = usersStore() const leads = createListResource({ type: 'list', doctype: 'CRM Lead', - cache: ['leads', props.organization.name], + cache: ['leads', props.organizationId], fields: [ 'name', 'first_name', @@ -305,7 +354,7 @@ const leads = createListResource({ 'modified', ], filters: { - organization: props.organization.name, + organization: props.organizationId, converted: 0, }, orderBy: 'modified desc', @@ -316,7 +365,7 @@ const leads = createListResource({ const deals = createListResource({ type: 'list', doctype: 'CRM Deal', - cache: ['deals', props.organization.name], + cache: ['deals', props.organizationId], fields: [ 'name', 'organization', @@ -328,7 +377,7 @@ const deals = createListResource({ 'modified', ], filters: { - organization: props.organization.name, + organization: props.organizationId, }, orderBy: 'modified desc', pageLength: 20, @@ -338,7 +387,7 @@ const deals = createListResource({ const contacts = createListResource({ type: 'list', doctype: 'Contact', - cache: ['contacts', props.organization.name], + cache: ['contacts', props.organizationId], fields: [ 'name', 'full_name', @@ -349,7 +398,7 @@ const contacts = createListResource({ 'modified', ], filters: { - company_name: props.organization.name, + company_name: props.organizationId, }, orderBy: 'modified desc', pageLength: 20, @@ -566,11 +615,4 @@ function reload(val) { deals.reload() contacts.reload() } - -watch( - () => props.organization.name, - (val) => val && reload(val) -) - -onMounted(() => reload(props.organization.name)) diff --git a/frontend/src/pages/Organizations.vue b/frontend/src/pages/Organizations.vue index a0d61642..b6e8fb47 100644 --- a/frontend/src/pages/Organizations.vue +++ b/frontend/src/pages/Organizations.vue @@ -10,56 +10,34 @@ @click="showOrganizationModal = true" > - Create organization - - - - - - - - {{ organization.name }} - - {{ - website(organization.website) - }} - - - + + + + + + + + + + + + + + - - - - - No organization selected - + + + + + import LayoutHeader from '@/components/LayoutHeader.vue' import OrganizationModal from '@/components/Modals/OrganizationModal.vue' -import OrganizationsIcon from '@/components/Icons/OrganizationsIcon.vue' -import { FeatherIcon, Breadcrumbs, Avatar } from 'frappe-ui' +import OrganizationsListView from '@/components/ListViews/OrganizationsListView.vue' +import SortBy from '@/components/SortBy.vue' +import Filter from '@/components/Filter.vue' +import { FeatherIcon, Breadcrumbs, Dropdown } from 'frappe-ui' import { organizationsStore } from '@/stores/organizations.js' -import { ref, computed, onMounted } from 'vue' +import { dateFormat, dateTooltipFormat, timeAgo, formatNumberIntoCurrency } from '@/utils' +import { ref, computed } from 'vue' import { useRoute } from 'vue-router' + const { organizations } = organizationsStore() const route = useRoute() + const showOrganizationModal = ref(false) + const currentOrganization = computed(() => { return organizations.data.find( (organization) => organization.name === route.params.organizationId ) }) + const breadcrumbs = computed(() => { let items = [{ label: 'Organizations', route: { name: 'Organizations' } }] if (!currentOrganization.value) return items @@ -94,13 +79,102 @@ const breadcrumbs = computed(() => { }) return items }) -onMounted(() => { - const el = document.querySelector('.router-link-active') - if (el) - setTimeout(() => { - el.scrollIntoView({ behavior: 'smooth', block: 'start' }) - }) + +const currentView = ref({ + label: 'List', + icon: 'list', }) + +const viewsDropdownOptions = [ + { + label: 'List', + icon: 'list', + onClick() { + currentView.value = { + label: 'List', + icon: 'list', + } + }, + }, + { + label: 'Table', + icon: 'grid', + onClick() { + currentView.value = { + label: 'Table', + icon: 'grid', + } + }, + }, + { + label: 'Calender', + icon: 'calendar', + onClick() { + currentView.value = { + label: 'Calender', + icon: 'calendar', + } + }, + }, + { + label: 'Board', + icon: 'columns', + onClick() { + currentView.value = { + label: 'Board', + icon: 'columns', + } + }, + }, +] + +const rows = computed(() => { + return organizations.data.map((organization) => { + return { + name: organization.name, + organization: { + label: organization.organization_name, + logo: organization.organization_logo, + }, + website: website(organization.website), + industry: organization.industry, + annual_revenue: formatNumberIntoCurrency(organization.annual_revenue), + modified: { + label: dateFormat(organization.modified, dateTooltipFormat), + timeAgo: timeAgo(organization.modified), + }, + } + }) +}) + +const columns = [ + { + label: 'Organization', + key: 'organization', + width: '16rem', + }, + { + label: 'Website', + key: 'website', + width: '14rem', + }, + { + label: 'Industry', + key: 'industry', + width: '14rem', + }, + { + label: 'Annual Revenue', + key: 'annual_revenue', + width: '14rem', + }, + { + label: 'Last modified', + key: 'modified', + width: '8rem', + }, +] + function website(url) { return url && url.replace(/^(?:https?:\/\/)?(?:www\.)?/i, '') } diff --git a/frontend/src/router.js b/frontend/src/router.js index d358c6b2..b90a00f9 100644 --- a/frontend/src/router.js +++ b/frontend/src/router.js @@ -49,14 +49,12 @@ const routes = [ path: '/organizations', name: 'Organizations', component: () => import('@/pages/Organizations.vue'), - children: [ - { - path: '/organizations/:organizationId?', - name: 'Organization', - component: () => import('@/pages/Organization.vue'), - props: true, - }, - ], + }, + { + path: '/organizations/:organizationId', + name: 'Organization', + component: () => import('@/pages/Organization.vue'), + props: true, }, { path: '/call-logs',