diff --git a/app/javascript/dashboard/api/companies.js b/app/javascript/dashboard/api/companies.js index b98a59983..a45b21d68 100644 --- a/app/javascript/dashboard/api/companies.js +++ b/app/javascript/dashboard/api/companies.js @@ -28,6 +28,14 @@ class CompanyAPI extends ApiClient { return axios.get(`${this.url}/${id}/contacts?${buildParams({ page })}`); } + listNotes(id) { + return axios.get(`${this.url}/${id}/notes`); + } + + listConversations(id) { + return axios.get(`${this.url}/${id}/conversations`); + } + searchContacts(id, query = '', page = 1) { const requestURL = `${this.url}/${id}/contacts/search?${buildParams({ q: query, page })}`; return axios.get(requestURL); diff --git a/app/javascript/dashboard/components-next/Companies/CompaniesDetailsLayout.vue b/app/javascript/dashboard/components-next/Companies/CompaniesDetailsLayout.vue index 7736ccffd..14b5e62d9 100644 --- a/app/javascript/dashboard/components-next/Companies/CompaniesDetailsLayout.vue +++ b/app/javascript/dashboard/components-next/Companies/CompaniesDetailsLayout.vue @@ -56,9 +56,14 @@ const closeMobileSidebar = () => {
+ {{ t('COMPANIES.DETAIL.HISTORY.EMPTY') }} +
+ diff --git a/app/javascript/dashboard/components-next/Companies/CompanyDetail/CompanyNotesSidebar.vue b/app/javascript/dashboard/components-next/Companies/CompanyDetail/CompanyNotesSidebar.vue new file mode 100644 index 000000000..234e0cd12 --- /dev/null +++ b/app/javascript/dashboard/components-next/Companies/CompanyDetail/CompanyNotesSidebar.vue @@ -0,0 +1,114 @@ + + + ++ {{ t('COMPANIES.DETAIL.NOTES.EMPTY') }} +
+ diff --git a/app/javascript/dashboard/i18n/locale/en/companies.json b/app/javascript/dashboard/i18n/locale/en/companies.json index 1fe8bc169..534205038 100644 --- a/app/javascript/dashboard/i18n/locale/en/companies.json +++ b/app/javascript/dashboard/i18n/locale/en/companies.json @@ -44,9 +44,17 @@ "SIDEBAR": { "TABS": { "ATTRIBUTES": "Attributes", - "CONTACTS": "Contacts" + "CONTACTS": "Contacts", + "HISTORY": "History", + "NOTES": "Notes" } }, + "HISTORY": { + "EMPTY": "No conversations found for this company's contacts yet." + }, + "NOTES": { + "EMPTY": "No notes found for this company's contacts yet." + }, "ATTRIBUTES": { "SEARCH_PLACEHOLDER": "Search attributes...", "EMPTY_STATE": "There are no company custom attributes configured yet.", diff --git a/app/javascript/dashboard/routes/dashboard/companies/pages/CompanyDetailView.vue b/app/javascript/dashboard/routes/dashboard/companies/pages/CompanyDetailView.vue index 313df49d3..86627f952 100644 --- a/app/javascript/dashboard/routes/dashboard/companies/pages/CompanyDetailView.vue +++ b/app/javascript/dashboard/routes/dashboard/companies/pages/CompanyDetailView.vue @@ -8,7 +8,10 @@ import Policy from 'dashboard/components/policy.vue'; import Button from 'dashboard/components-next/button/Button.vue'; import CompaniesDetailsLayout from 'dashboard/components-next/Companies/CompaniesDetailsLayout.vue'; import Spinner from 'dashboard/components-next/spinner/Spinner.vue'; +import TabBar from 'dashboard/components-next/tabbar/TabBar.vue'; import CompanyContactsSidebar from 'dashboard/components-next/Companies/CompanyDetail/CompanyContactsSidebar.vue'; +import CompanyHistorySidebar from 'dashboard/components-next/Companies/CompanyDetail/CompanyHistorySidebar.vue'; +import CompanyNotesSidebar from 'dashboard/components-next/Companies/CompanyDetail/CompanyNotesSidebar.vue'; import CompanyProfileCard from 'dashboard/components-next/Companies/CompanyDetail/CompanyProfileCard.vue'; import ConfirmCompanyDeleteDialog from 'dashboard/components-next/Companies/CompanyDetail/ConfirmCompanyDeleteDialog.vue'; import { useCompaniesStore } from 'dashboard/stores/companies'; @@ -20,11 +23,16 @@ const { t } = useI18n(); const confirmDeleteDialogRef = ref(null); const selectedCandidate = ref(null); +const activeSidebarTab = ref('history'); const companyId = computed(() => Number(route.params.companyId)); const company = computed(() => companiesStore.getRecord(companyId.value)); const companyContacts = computed(() => companiesStore.companyContacts); const companyContactsMeta = computed(() => companiesStore.companyContactsMeta); +const companyConversations = computed( + () => companiesStore.companyConversations || [] +); +const companyNotes = computed(() => companiesStore.companyNotes || []); const contactSearchResults = computed( () => companiesStore.contactSearchResults ); @@ -32,6 +40,10 @@ const uiFlags = computed(() => companiesStore.getUIFlags); const isFetchingCompany = computed(() => uiFlags.value.fetchingItem); const isFetchingContacts = computed(() => uiFlags.value.fetchingContacts); +const isFetchingConversations = computed( + () => uiFlags.value.fetchingConversations +); +const isFetchingNotes = computed(() => uiFlags.value.fetchingNotes); const isSearchingContacts = computed(() => uiFlags.value.searchingContacts); const isManagingContacts = computed( () => uiFlags.value.creatingContact || uiFlags.value.removingContact @@ -50,6 +62,27 @@ const breadcrumbItems = computed(() => [ : []), ]); +const SIDEBAR_TABS_OPTIONS = [ + { key: 'HISTORY', value: 'history' }, + { key: 'NOTES', value: 'notes' }, + { key: 'CONTACTS', value: 'contacts' }, +]; + +const sidebarTabs = computed(() => + SIDEBAR_TABS_OPTIONS.map(tab => ({ + label: { + notes: t('COMPANIES.DETAIL.SIDEBAR.TABS.NOTES'), + history: t('COMPANIES.DETAIL.SIDEBAR.TABS.HISTORY'), + contacts: `${t('COMPANIES.DETAIL.SIDEBAR.TABS.CONTACTS')} (${Number(companyContactsMeta.value.totalCount || 0)})`, + }[tab.value], + value: tab.value, + })) +); + +const activeSidebarTabIndex = computed(() => + SIDEBAR_TABS_OPTIONS.findIndex(tab => tab.value === activeSidebarTab.value) +); + const goToCompaniesIndex = () => { router.push({ name: 'companies_dashboard_index', @@ -79,6 +112,19 @@ const clearSelectedCandidate = () => { selectedCandidate.value = null; }; +const loadSidebarTab = tab => { + if (!companyId.value) return; + if (tab === 'notes') companiesStore.getCompanyNotes(companyId.value); + if (tab === 'history') { + companiesStore.getCompanyConversations(companyId.value); + } +}; + +const handleSidebarTabChange = tab => { + activeSidebarTab.value = tab.value; + loadSidebarTab(tab.value); +}; + const handleContactSearch = async query => { await companiesStore.searchCompanyContactCandidates({ companyId: companyId.value, @@ -143,10 +189,12 @@ watch( async id => { companiesStore.resetCompanyDetailState(); clearSelectedCandidate(); + activeSidebarTab.value = 'history'; if (!id) return; await Promise.allSettled([ companiesStore.show(id), companiesStore.getCompanyContacts(id), + companiesStore.getCompanyConversations(id), ]); }, { immediate: true } @@ -207,8 +255,29 @@ onBeforeUnmount(() => {