added pagination for the leads tasks and deals accounts
This commit is contained in:
parent
e91bbbbd1d
commit
e53b229c03
@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect, useMemo } from 'react';
|
||||
import React, { useState, useEffect, useMemo, useCallback } from 'react';
|
||||
import {
|
||||
View,
|
||||
Text,
|
||||
@ -8,6 +8,7 @@ import {
|
||||
RefreshControl,
|
||||
FlatList,
|
||||
Alert,
|
||||
ActivityIndicator,
|
||||
} from 'react-native';
|
||||
import { useSelector, useDispatch } from 'react-redux';
|
||||
import type { AppDispatch } from '@/store/store';
|
||||
@ -26,9 +27,26 @@ import {
|
||||
selectPurchaseOrders,
|
||||
selectInvoices,
|
||||
selectCrmLoading,
|
||||
selectCrmErrors
|
||||
selectCrmErrors,
|
||||
selectLeadsPagination,
|
||||
selectTasksPagination,
|
||||
selectContactsPagination,
|
||||
selectDealsPagination,
|
||||
selectSalesOrdersPagination,
|
||||
selectPurchaseOrdersPagination,
|
||||
selectInvoicesPagination
|
||||
} from '../store/selectors';
|
||||
import { fetchAllCrmData } from '../store/crmSlice';
|
||||
import {
|
||||
fetchAllCrmData,
|
||||
fetchLeads,
|
||||
fetchTasks,
|
||||
fetchContacts,
|
||||
fetchDeals,
|
||||
resetLeadsPagination,
|
||||
resetTasksPagination,
|
||||
resetContactsPagination,
|
||||
resetDealsPagination
|
||||
} from '../store/crmSlice';
|
||||
import type { RootState } from '@/store/store';
|
||||
|
||||
const ZohoCrmDataScreen: React.FC = () => {
|
||||
@ -47,6 +65,15 @@ const ZohoCrmDataScreen: React.FC = () => {
|
||||
const invoices = useSelector(selectInvoices);
|
||||
const loading = useSelector(selectCrmLoading);
|
||||
const errors = useSelector(selectCrmErrors);
|
||||
|
||||
// Pagination selectors
|
||||
const leadsPagination = useSelector(selectLeadsPagination);
|
||||
const tasksPagination = useSelector(selectTasksPagination);
|
||||
const contactsPagination = useSelector(selectContactsPagination);
|
||||
const dealsPagination = useSelector(selectDealsPagination);
|
||||
const salesOrdersPagination = useSelector(selectSalesOrdersPagination);
|
||||
const purchaseOrdersPagination = useSelector(selectPurchaseOrdersPagination);
|
||||
const invoicesPagination = useSelector(selectInvoicesPagination);
|
||||
|
||||
// Create CRM data object from Redux state
|
||||
const crmData: CrmData = useMemo(() => ({
|
||||
@ -60,7 +87,7 @@ const ZohoCrmDataScreen: React.FC = () => {
|
||||
}), [leads, tasks, contacts, deals, salesOrders, purchaseOrders, invoices]);
|
||||
|
||||
// Fetch CRM data using Redux
|
||||
const fetchCrmData = async (showRefresh = false) => {
|
||||
const fetchCrmData = useCallback(async (showRefresh = false) => {
|
||||
try {
|
||||
if (showRefresh) {
|
||||
setRefreshing(true);
|
||||
@ -78,23 +105,87 @@ const ZohoCrmDataScreen: React.FC = () => {
|
||||
} finally {
|
||||
setRefreshing(false);
|
||||
}
|
||||
};
|
||||
}, [dispatch]);
|
||||
|
||||
// Load more data for infinite scrolling
|
||||
const loadMoreData = useCallback(async (dataType: string) => {
|
||||
try {
|
||||
let pagination;
|
||||
let fetchAction;
|
||||
let resetAction;
|
||||
|
||||
switch (dataType) {
|
||||
case 'leads':
|
||||
pagination = leadsPagination;
|
||||
fetchAction = fetchLeads;
|
||||
resetAction = resetLeadsPagination;
|
||||
break;
|
||||
case 'tasks':
|
||||
pagination = tasksPagination;
|
||||
fetchAction = fetchTasks;
|
||||
resetAction = resetTasksPagination;
|
||||
break;
|
||||
case 'contacts':
|
||||
pagination = contactsPagination;
|
||||
fetchAction = fetchContacts;
|
||||
resetAction = resetContactsPagination;
|
||||
break;
|
||||
case 'deals':
|
||||
pagination = dealsPagination;
|
||||
fetchAction = fetchDeals;
|
||||
resetAction = resetDealsPagination;
|
||||
break;
|
||||
default:
|
||||
return; // Only leads, tasks, contacts, and deals support infinite scrolling for now
|
||||
}
|
||||
|
||||
// Check if there are more records and not currently loading
|
||||
if (!pagination.moreRecords || loading[dataType as keyof typeof loading]) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Fetch next page
|
||||
await (dispatch(fetchAction({
|
||||
page: pagination.page + 1,
|
||||
limit: 20,
|
||||
append: true
|
||||
}) as any)).unwrap();
|
||||
|
||||
} catch (err) {
|
||||
showError(`Failed to load more ${dataType}`);
|
||||
}
|
||||
}, [dispatch, leadsPagination, tasksPagination, contactsPagination, dealsPagination, loading]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchCrmData();
|
||||
}, []);
|
||||
|
||||
const handleRefresh = () => {
|
||||
const handleRefresh = useCallback(() => {
|
||||
fetchCrmData(true);
|
||||
};
|
||||
}, [fetchCrmData]);
|
||||
|
||||
const handleRetry = () => {
|
||||
const handleRetry = useCallback(() => {
|
||||
fetchCrmData();
|
||||
};
|
||||
}, [fetchCrmData]);
|
||||
|
||||
const handleCardPress = (item: any, type: string) => {
|
||||
const handleCardPress = useCallback((item: any, type: string) => {
|
||||
showInfo(`Viewing ${type}: ${item.name || item.subject || `${item.firstName} ${item.lastName}`}`);
|
||||
};
|
||||
}, []);
|
||||
|
||||
// Render loading footer for infinite scroll
|
||||
const renderFooter = useCallback((dataType: string) => {
|
||||
const isLoadingMore = loading[dataType as keyof typeof loading];
|
||||
if (!isLoadingMore) return null;
|
||||
|
||||
return (
|
||||
<View style={styles.footerLoader}>
|
||||
<ActivityIndicator size="small" color={colors.primary} />
|
||||
<Text style={[styles.footerText, { color: colors.textLight }]}>
|
||||
Loading more...
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}, [loading, colors]);
|
||||
|
||||
// Get current loading state and error
|
||||
const isLoading = loading.leads || loading.tasks || loading.contacts || loading.deals ||
|
||||
@ -123,7 +214,7 @@ const ZohoCrmDataScreen: React.FC = () => {
|
||||
}
|
||||
|
||||
|
||||
const renderTabContent = () => {
|
||||
const renderTabContent = useCallback(() => {
|
||||
const commonFlatListProps = {
|
||||
numColumns: 1,
|
||||
showsVerticalScrollIndicator: false,
|
||||
@ -144,7 +235,10 @@ const ZohoCrmDataScreen: React.FC = () => {
|
||||
onPress={() => handleCardPress(item, 'Lead')}
|
||||
/>
|
||||
)}
|
||||
keyExtractor={(item) => item.id}
|
||||
keyExtractor={(item) => `lead-${item.id}`}
|
||||
onEndReached={() => loadMoreData('leads')}
|
||||
onEndReachedThreshold={0.1}
|
||||
ListFooterComponent={() => renderFooter('leads')}
|
||||
{...commonFlatListProps}
|
||||
/>
|
||||
);
|
||||
@ -158,7 +252,10 @@ const ZohoCrmDataScreen: React.FC = () => {
|
||||
onPress={() => handleCardPress(item, 'Task')}
|
||||
/>
|
||||
)}
|
||||
keyExtractor={(item) => item.id}
|
||||
keyExtractor={(item) => `task-${item.id}`}
|
||||
onEndReached={() => loadMoreData('tasks')}
|
||||
onEndReachedThreshold={0.1}
|
||||
ListFooterComponent={() => renderFooter('tasks')}
|
||||
{...commonFlatListProps}
|
||||
/>
|
||||
);
|
||||
@ -172,7 +269,10 @@ const ZohoCrmDataScreen: React.FC = () => {
|
||||
onPress={() => handleCardPress(item, 'Contact')}
|
||||
/>
|
||||
)}
|
||||
keyExtractor={(item) => item.id}
|
||||
keyExtractor={(item) => `contact-${item.id}`}
|
||||
onEndReached={() => loadMoreData('contacts')}
|
||||
onEndReachedThreshold={0.1}
|
||||
ListFooterComponent={() => renderFooter('contacts')}
|
||||
{...commonFlatListProps}
|
||||
/>
|
||||
);
|
||||
@ -186,7 +286,10 @@ const ZohoCrmDataScreen: React.FC = () => {
|
||||
onPress={() => handleCardPress(item, 'Deal')}
|
||||
/>
|
||||
)}
|
||||
keyExtractor={(item) => item.id}
|
||||
keyExtractor={(item) => `deal-${item.id}`}
|
||||
onEndReached={() => loadMoreData('deals')}
|
||||
onEndReachedThreshold={0.1}
|
||||
ListFooterComponent={() => renderFooter('deals')}
|
||||
{...commonFlatListProps}
|
||||
/>
|
||||
);
|
||||
@ -200,7 +303,7 @@ const ZohoCrmDataScreen: React.FC = () => {
|
||||
onPress={() => handleCardPress(item, 'Sales Order')}
|
||||
/>
|
||||
)}
|
||||
keyExtractor={(item) => item.id}
|
||||
keyExtractor={(item) => `sales-order-${item.id}`}
|
||||
{...commonFlatListProps}
|
||||
/>
|
||||
);
|
||||
@ -214,7 +317,7 @@ const ZohoCrmDataScreen: React.FC = () => {
|
||||
onPress={() => handleCardPress(item, 'Purchase Order')}
|
||||
/>
|
||||
)}
|
||||
keyExtractor={(item) => item.id}
|
||||
keyExtractor={(item) => `purchase-order-${item.id}`}
|
||||
{...commonFlatListProps}
|
||||
/>
|
||||
);
|
||||
@ -228,14 +331,14 @@ const ZohoCrmDataScreen: React.FC = () => {
|
||||
onPress={() => handleCardPress(item, 'Invoice')}
|
||||
/>
|
||||
)}
|
||||
keyExtractor={(item) => item.id}
|
||||
keyExtractor={(item) => `invoice-${item.id}`}
|
||||
{...commonFlatListProps}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}, [selectedTab, crmData, handleCardPress, loadMoreData, renderFooter, refreshing, handleRefresh]);
|
||||
|
||||
return (
|
||||
<View style={[styles.container, { backgroundColor: colors.background }]}>
|
||||
@ -366,6 +469,16 @@ const styles = StyleSheet.create({
|
||||
paddingHorizontal: 16,
|
||||
paddingBottom: 20,
|
||||
},
|
||||
footerLoader: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
paddingVertical: 16,
|
||||
},
|
||||
footerText: {
|
||||
marginLeft: 8,
|
||||
fontSize: 14,
|
||||
},
|
||||
});
|
||||
|
||||
export default ZohoCrmDataScreen;
|
||||
|
||||
@ -25,8 +25,8 @@ export const crmAPI = {
|
||||
provider: 'zoho',
|
||||
service: 'crm',
|
||||
resource,
|
||||
page: 1,
|
||||
limit: 20,
|
||||
page: params?.page || 1,
|
||||
limit: params?.limit || 20,
|
||||
...params
|
||||
};
|
||||
|
||||
@ -47,13 +47,34 @@ export const crmAPI = {
|
||||
crmAPI.getCrmData<CrmDeal>('deals', params),
|
||||
|
||||
// New API endpoints for sales orders, purchase orders, and invoices
|
||||
getSalesOrders: (params?: CrmSearchParams) =>
|
||||
http.get<CrmApiResponse<CrmPaginatedResponse<any>>>(`/api/v1/integrations/zoho/crm/sales-orders?provider=zoho`, params),
|
||||
getSalesOrders: (params?: CrmSearchParams) => {
|
||||
const queryParams = {
|
||||
provider: 'zoho',
|
||||
page: params?.page || 1,
|
||||
limit: params?.limit || 20,
|
||||
...params
|
||||
};
|
||||
return http.get<CrmApiResponse<CrmPaginatedResponse<any>>>(`/api/v1/integrations/zoho/crm/sales-orders`, queryParams);
|
||||
},
|
||||
|
||||
getPurchaseOrders: (params?: CrmSearchParams) =>
|
||||
http.get<CrmApiResponse<CrmPaginatedResponse<any>>>(`/api/v1/integrations/zoho/crm/purchase-orders?provider=zoho`, params),
|
||||
getPurchaseOrders: (params?: CrmSearchParams) => {
|
||||
const queryParams = {
|
||||
provider: 'zoho',
|
||||
page: params?.page || 1,
|
||||
limit: params?.limit || 20,
|
||||
...params
|
||||
};
|
||||
return http.get<CrmApiResponse<CrmPaginatedResponse<any>>>(`/api/v1/integrations/zoho/crm/purchase-orders`, queryParams);
|
||||
},
|
||||
|
||||
getInvoices: (params?: CrmSearchParams) =>
|
||||
http.get<CrmApiResponse<CrmPaginatedResponse<any>>>(`/api/v1/integrations/zoho/crm/invoices?provider=zoho`, params),
|
||||
getInvoices: (params?: CrmSearchParams) => {
|
||||
const queryParams = {
|
||||
provider: 'zoho',
|
||||
page: params?.page || 1,
|
||||
limit: params?.limit || 20,
|
||||
...params
|
||||
};
|
||||
return http.get<CrmApiResponse<CrmPaginatedResponse<any>>>(`/api/v1/integrations/zoho/crm/invoices`, queryParams);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@ -127,36 +127,48 @@ const initialState: CrmState = {
|
||||
},
|
||||
};
|
||||
|
||||
// Async thunks
|
||||
// Async thunks for infinite scrolling
|
||||
export const fetchLeads = createAsyncThunk(
|
||||
'crm/fetchLeads',
|
||||
async (params?: CrmSearchParams) => {
|
||||
async (params?: CrmSearchParams & { append?: boolean }) => {
|
||||
const response = await crmAPI.getLeads(params);
|
||||
return response.data?.data || { data: [], info: { count: 0, moreRecords: false, page: 1 } };
|
||||
return {
|
||||
data: response.data?.data || { data: [], info: { count: 0, moreRecords: false, page: 1 } },
|
||||
append: params?.append || false
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
export const fetchTasks = createAsyncThunk(
|
||||
'crm/fetchTasks',
|
||||
async (params?: CrmSearchParams) => {
|
||||
async (params?: CrmSearchParams & { append?: boolean }) => {
|
||||
const response = await crmAPI.getTasks(params);
|
||||
return response.data?.data || { data: [], info: { count: 0, moreRecords: false, page: 1 } };
|
||||
return {
|
||||
data: response.data?.data || { data: [], info: { count: 0, moreRecords: false, page: 1 } },
|
||||
append: params?.append || false
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
export const fetchContacts = createAsyncThunk(
|
||||
'crm/fetchContacts',
|
||||
async (params?: CrmSearchParams) => {
|
||||
async (params?: CrmSearchParams & { append?: boolean }) => {
|
||||
const response = await crmAPI.getContacts(params);
|
||||
return response.data?.data || { data: [], info: { count: 0, moreRecords: false, page: 1 } };
|
||||
return {
|
||||
data: response.data?.data || { data: [], info: { count: 0, moreRecords: false, page: 1 } },
|
||||
append: params?.append || false
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
export const fetchDeals = createAsyncThunk(
|
||||
'crm/fetchDeals',
|
||||
async (params?: CrmSearchParams) => {
|
||||
async (params?: CrmSearchParams & { append?: boolean }) => {
|
||||
const response = await crmAPI.getDeals(params);
|
||||
return response.data?.data || { data: [], info: { count: 0, moreRecords: false, page: 1 } };
|
||||
return {
|
||||
data: response.data?.data || { data: [], info: { count: 0, moreRecords: false, page: 1 } },
|
||||
append: params?.append || false
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
@ -266,6 +278,23 @@ const crmSlice = createSlice({
|
||||
setInvoicesPage: (state, action: PayloadAction<number>) => {
|
||||
state.pagination.invoices.page = action.payload;
|
||||
},
|
||||
// Reset pagination for specific data types
|
||||
resetLeadsPagination: (state) => {
|
||||
state.pagination.leads = { page: 1, count: 0, moreRecords: false };
|
||||
state.leads = [];
|
||||
},
|
||||
resetTasksPagination: (state) => {
|
||||
state.pagination.tasks = { page: 1, count: 0, moreRecords: false };
|
||||
state.tasks = [];
|
||||
},
|
||||
resetContactsPagination: (state) => {
|
||||
state.pagination.contacts = { page: 1, count: 0, moreRecords: false };
|
||||
state.contacts = [];
|
||||
},
|
||||
resetDealsPagination: (state) => {
|
||||
state.pagination.deals = { page: 1, count: 0, moreRecords: false };
|
||||
state.deals = [];
|
||||
},
|
||||
},
|
||||
extraReducers: (builder) => {
|
||||
// Fetch leads
|
||||
@ -276,8 +305,17 @@ const crmSlice = createSlice({
|
||||
})
|
||||
.addCase(fetchLeads.fulfilled, (state, action) => {
|
||||
state.loading.leads = false;
|
||||
state.leads = action.payload.data || [];
|
||||
state.pagination.leads = action.payload.info || { page: 1, count: 0, moreRecords: false };
|
||||
const { data, append } = action.payload;
|
||||
|
||||
if (append) {
|
||||
// Append new data to existing data for infinite scrolling
|
||||
state.leads = [...state.leads, ...(data.data || [])];
|
||||
} else {
|
||||
// Replace data for initial load or refresh
|
||||
state.leads = data.data || [];
|
||||
}
|
||||
|
||||
state.pagination.leads = data.info || { page: 1, count: 0, moreRecords: false };
|
||||
state.lastUpdated.leads = new Date().toISOString();
|
||||
})
|
||||
.addCase(fetchLeads.rejected, (state, action) => {
|
||||
@ -292,8 +330,17 @@ const crmSlice = createSlice({
|
||||
})
|
||||
.addCase(fetchTasks.fulfilled, (state, action) => {
|
||||
state.loading.tasks = false;
|
||||
state.tasks = action.payload.data || [];
|
||||
state.pagination.tasks = action.payload.info || { page: 1, count: 0, moreRecords: false };
|
||||
const { data, append } = action.payload;
|
||||
|
||||
if (append) {
|
||||
// Append new data to existing data for infinite scrolling
|
||||
state.tasks = [...state.tasks, ...(data.data || [])];
|
||||
} else {
|
||||
// Replace data for initial load or refresh
|
||||
state.tasks = data.data || [];
|
||||
}
|
||||
|
||||
state.pagination.tasks = data.info || { page: 1, count: 0, moreRecords: false };
|
||||
state.lastUpdated.tasks = new Date().toISOString();
|
||||
})
|
||||
.addCase(fetchTasks.rejected, (state, action) => {
|
||||
@ -308,8 +355,17 @@ const crmSlice = createSlice({
|
||||
})
|
||||
.addCase(fetchContacts.fulfilled, (state, action) => {
|
||||
state.loading.contacts = false;
|
||||
state.contacts = action.payload.data || [];
|
||||
state.pagination.contacts = action.payload.info || { page: 1, count: 0, moreRecords: false };
|
||||
const { data, append } = action.payload;
|
||||
|
||||
if (append) {
|
||||
// Append new data to existing data for infinite scrolling
|
||||
state.contacts = [...state.contacts, ...(data.data || [])];
|
||||
} else {
|
||||
// Replace data for initial load or refresh
|
||||
state.contacts = data.data || [];
|
||||
}
|
||||
|
||||
state.pagination.contacts = data.info || { page: 1, count: 0, moreRecords: false };
|
||||
state.lastUpdated.contacts = new Date().toISOString();
|
||||
})
|
||||
.addCase(fetchContacts.rejected, (state, action) => {
|
||||
@ -324,8 +380,17 @@ const crmSlice = createSlice({
|
||||
})
|
||||
.addCase(fetchDeals.fulfilled, (state, action) => {
|
||||
state.loading.deals = false;
|
||||
state.deals = action.payload.data || [];
|
||||
state.pagination.deals = action.payload.info || { page: 1, count: 0, moreRecords: false };
|
||||
const { data, append } = action.payload;
|
||||
|
||||
if (append) {
|
||||
// Append new data to existing data for infinite scrolling
|
||||
state.deals = [...state.deals, ...(data.data || [])];
|
||||
} else {
|
||||
// Replace data for initial load or refresh
|
||||
state.deals = data.data || [];
|
||||
}
|
||||
|
||||
state.pagination.deals = data.info || { page: 1, count: 0, moreRecords: false };
|
||||
state.lastUpdated.deals = new Date().toISOString();
|
||||
})
|
||||
.addCase(fetchDeals.rejected, (state, action) => {
|
||||
@ -465,6 +530,10 @@ export const {
|
||||
setSalesOrdersPage,
|
||||
setPurchaseOrdersPage,
|
||||
setInvoicesPage,
|
||||
resetLeadsPagination,
|
||||
resetTasksPagination,
|
||||
resetContactsPagination,
|
||||
resetDealsPagination,
|
||||
} = crmSlice.actions;
|
||||
|
||||
export default crmSlice.reducer;
|
||||
|
||||
@ -17,6 +17,15 @@ export const selectCrmLoading = (state: RootState) => state.crm.loading;
|
||||
export const selectCrmErrors = (state: RootState) => state.crm.errors;
|
||||
export const selectCrmPagination = (state: RootState) => state.crm.pagination;
|
||||
|
||||
// Pagination selectors for infinite scrolling
|
||||
export const selectLeadsPagination = (state: RootState) => state.crm.pagination.leads;
|
||||
export const selectTasksPagination = (state: RootState) => state.crm.pagination.tasks;
|
||||
export const selectContactsPagination = (state: RootState) => state.crm.pagination.contacts;
|
||||
export const selectDealsPagination = (state: RootState) => state.crm.pagination.deals;
|
||||
export const selectSalesOrdersPagination = (state: RootState) => state.crm.pagination.salesOrders;
|
||||
export const selectPurchaseOrdersPagination = (state: RootState) => state.crm.pagination.purchaseOrders;
|
||||
export const selectInvoicesPagination = (state: RootState) => state.crm.pagination.invoices;
|
||||
|
||||
// Loading selectors
|
||||
export const selectLeadsLoading = (state: RootState) => state.crm.loading.leads;
|
||||
export const selectTasksLoading = (state: RootState) => state.crm.loading.tasks;
|
||||
|
||||
@ -8,7 +8,7 @@ import { clearSelectedService } from '@/modules/integrations/store/integrationsS
|
||||
let pendingRequest: any = null;
|
||||
|
||||
const http = create({
|
||||
baseURL: 'http://192.168.1.17:4000',
|
||||
baseURL: 'http://10.175.59.235:4000',
|
||||
// baseURL: 'http://160.187.167.216',
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user