@@ -24,18 +24,37 @@ export const CompaniesPage: React.FC = () => {
2424 const [ isViewModalOpen , setIsViewModalOpen ] = useState ( false ) ;
2525 const [ isEditModalOpen , setIsEditModalOpen ] = useState ( false ) ;
2626 const [ deleteId , setDeleteId ] = useState < number | null > ( null ) ;
27+
28+ // Track which tabs have been loaded to avoid duplicate calls
29+ const [ loadedTabs , setLoadedTabs ] = useState < Set < string > > ( new Set ( [ 'active' ] ) ) ;
2730
2831 const [ filters , setFilters ] = useState < CompanyFilterState > ( {
2932 search : '' ,
3033 status : '' ,
3134 workType : ''
3235 } ) ;
3336
34- const fetchData = async ( ) => {
37+ const fetchDataForTab = async ( tab : 'active' | 'dropped' | 'past' ) => {
3538 setIsLoading ( true ) ;
3639 try {
37- const data = await companiesApi . getAll ( ) ;
40+ let data : CRMEntry [ ] ;
41+
42+ switch ( tab ) {
43+ case 'active' :
44+ data = await companiesApi . getOnboarded ( ) ;
45+ break ;
46+ case 'dropped' :
47+ data = await companiesApi . getClosed ( ) ;
48+ break ;
49+ case 'past' :
50+ data = await companiesApi . getDone ( ) ;
51+ break ;
52+ default :
53+ data = [ ] ;
54+ }
55+
3856 setCrmEntries ( data ) ;
57+ setLoadedTabs ( prev => new Set ( prev ) . add ( tab ) ) ;
3958 } catch ( error ) {
4059 console . error ( "Failed to fetch data" , error ) ;
4160 showToast ( "Failed to load companies" , "error" ) ;
@@ -44,29 +63,28 @@ export const CompaniesPage: React.FC = () => {
4463 }
4564 } ;
4665
66+ // Initial load - fetch only onboarded (active) companies
4767 useEffect ( ( ) => {
48- fetchData ( ) ;
68+ let mounted = true ;
69+ const loadData = async ( ) => {
70+ if ( mounted ) {
71+ await fetchDataForTab ( 'active' ) ;
72+ }
73+ } ;
74+ loadData ( ) ;
75+ return ( ) => { mounted = false ; } ;
4976 } , [ ] ) ;
5077
51- const allCompanies = useMemo ( ( ) => {
52- // Registry now strictly includes those that have passed initial lead stages
53- return crmEntries . filter ( entry => [ 'onboarded' , 'on progress' , 'Quote Sent' , 'completed' , 'drop' ] . includes ( entry . status ) ) ;
54- } , [ crmEntries ] ) ;
55-
56- const categorizedData = useMemo ( ( ) => {
57- // Onboarded, On Progress, and Quote Sent are considered active Focus nodes in the registry
58- const active = allCompanies . filter ( c => [ 'onboarded' , 'on progress' , 'Quote Sent' ] . includes ( c . status ) ) ;
59- const dropped = allCompanies . filter ( c => c . status === 'drop' ) ;
60- const past = allCompanies . filter ( c => c . status === 'completed' ) ;
61- return { active, dropped, past } ;
62- } , [ allCompanies ] ) ;
78+ // Handle tab changes - fetch data only if not already loaded
79+ useEffect ( ( ) => {
80+ if ( ! loadedTabs . has ( activeTab ) ) {
81+ fetchDataForTab ( activeTab ) ;
82+ }
83+ } , [ activeTab ] ) ;
6384
85+ // No need for categorization - data comes pre-filtered from backend
6486 const displayData = useMemo ( ( ) => {
65- let sourceList = categorizedData . active ;
66- if ( activeTab === 'dropped' ) sourceList = categorizedData . dropped ;
67- if ( activeTab === 'past' ) sourceList = categorizedData . past ;
68-
69- let result = sourceList . filter ( item => {
87+ let result = crmEntries . filter ( item => {
7088 const matchesSearch = filters . search === '' ||
7189 ( item . company || '' ) . toLowerCase ( ) . includes ( filters . search . toLowerCase ( ) ) ||
7290 ( item . contactName && item . contactName . toLowerCase ( ) . includes ( filters . search . toLowerCase ( ) ) ) ||
@@ -79,7 +97,7 @@ export const CompaniesPage: React.FC = () => {
7997 } ) ;
8098
8199 return result . sort ( ( a , b ) => b . id - a . id ) ;
82- } , [ categorizedData , activeTab , filters ] ) ;
100+ } , [ crmEntries , filters ] ) ;
83101
84102 const handleEdit = ( company : CRMEntry ) => {
85103 setEditingCompany ( company ) ;
@@ -100,8 +118,10 @@ export const CompaniesPage: React.FC = () => {
100118 try {
101119 await companiesApi . update ( updatedEntry . id , updatedEntry ) ;
102120 showToast ( "Company details updated" , "success" ) ;
121+ // Refresh current tab data
122+ await fetchDataForTab ( activeTab ) ;
103123 } catch ( e ) {
104- fetchData ( ) ;
124+ await fetchDataForTab ( activeTab ) ;
105125 showToast ( "Failed to update company" , "error" ) ;
106126 }
107127 }
@@ -118,12 +138,15 @@ export const CompaniesPage: React.FC = () => {
118138 try {
119139 await companiesApi . update ( company . id , updatedEntry ) ;
120140 showToast ( `Status updated to ${ newStatus } ` , "success" ) ;
121- // If status changes out of registry criteria, re-fetch to update view
122- if ( ! [ 'onboarded' , 'on progress' , 'Quote Sent' , 'completed' , 'drop' ] . includes ( newStatus ) ) {
123- fetchData ( ) ;
124- }
141+
142+ // Status change may move company to different tab
143+ // Clear loaded tabs cache to force refresh when switching tabs
144+ setLoadedTabs ( new Set ( [ activeTab ] ) ) ;
145+
146+ // Refresh current tab to reflect changes
147+ await fetchDataForTab ( activeTab ) ;
125148 } catch ( e ) {
126- fetchData ( ) ;
149+ await fetchDataForTab ( activeTab ) ;
127150 showToast ( "Failed to update status" , "error" ) ;
128151 }
129152 } ;
@@ -170,7 +193,7 @@ export const CompaniesPage: React.FC = () => {
170193 } `}
171194 >
172195 Active Focus
173- < span className = "ml-2 px-1.5 lg:px-2 py-0.5 rounded bg-brand-50 text-brand-600 text-[9px] lg:text-[10px]" > { categorizedData . active . length } </ span >
196+ { activeTab === 'active' && < span className = "ml-2 px-1.5 lg:px-2 py-0.5 rounded bg-brand-50 text-brand-600 text-[9px] lg:text-[10px]" > { crmEntries . length } </ span > }
174197 </ button >
175198 < button
176199 onClick = { ( ) => setActiveTab ( 'past' ) }
@@ -181,7 +204,7 @@ export const CompaniesPage: React.FC = () => {
181204 } `}
182205 >
183206 Historical
184- < span className = "ml-2 px-1.5 lg:px-2 py-0.5 rounded bg-emerald-50 text-emerald-600 text-[9px] lg:text-[10px]" > { categorizedData . past . length } </ span >
207+ { activeTab === 'past' && < span className = "ml-2 px-1.5 lg:px-2 py-0.5 rounded bg-emerald-50 text-emerald-600 text-[9px] lg:text-[10px]" > { crmEntries . length } </ span > }
185208 </ button >
186209 < button
187210 onClick = { ( ) => setActiveTab ( 'dropped' ) }
@@ -192,12 +215,12 @@ export const CompaniesPage: React.FC = () => {
192215 } `}
193216 >
194217 Archived
195- < span className = "ml-2 px-1.5 lg:px-2 py-0.5 rounded bg-rose-50 text-rose-600 text-[9px] lg:text-[10px]" > { categorizedData . dropped . length } </ span >
218+ { activeTab === 'dropped' && < span className = "ml-2 px-1.5 lg:px-2 py-0.5 rounded bg-rose-50 text-rose-600 text-[9px] lg:text-[10px]" > { crmEntries . length } </ span > }
196219 </ button >
197220 </ div >
198221 </ div >
199222
200- < CompaniesFilters filters = { filters } setFilters = { setFilters } onRefresh = { fetchData } />
223+ < CompaniesFilters filters = { filters } setFilters = { setFilters } onRefresh = { ( ) => fetchDataForTab ( activeTab ) } />
201224
202225 < div className = "pt-4 pb-10 overflow-x-auto" >
203226 < CompaniesTable
0 commit comments