zoho peple screen modified and re authentication rmoved based on token availability

This commit is contained in:
yashwin-foxy 2025-09-22 20:20:21 +05:30
parent 54c751324c
commit e479b59ae3
4 changed files with 691 additions and 12 deletions

View File

@ -1,12 +1,14 @@
import React from 'react';
import { createStackNavigator } from '@react-navigation/stack';
import ZohoPeopleDashboardScreen from '@/modules/hr/zoho/screens/ZohoPeopleDashboardScreen';
import ZohoPeopleDataScreen from '@/modules/hr/zoho/screens/ZohoPeopleDataScreen';
const Stack = createStackNavigator();
const HRNavigator = () => (
<Stack.Navigator>
<Stack.Screen name="ZohoPeopleDashboard" component={ZohoPeopleDashboardScreen} options={{headerShown:false}} />
<Stack.Screen name="ZohoPeopleData" component={ZohoPeopleDataScreen} options={{headerShown:false}} />
</Stack.Navigator>
);

View File

@ -9,6 +9,7 @@ import {
Dimensions
} from 'react-native';
import { useDispatch, useSelector } from 'react-redux';
import { useNavigation } from '@react-navigation/native';
import Icon from 'react-native-vector-icons/MaterialIcons';
import { Container, LoadingSpinner, ErrorState } from '@/shared/components/ui';
import { fetchZohoPeopleData } from '../store/hrSlice';
@ -26,6 +27,7 @@ const { width } = Dimensions.get('window');
const ZohoPeopleDashboardScreen: React.FC = () => {
const dispatch = useDispatch();
const navigation = useNavigation();
const { colors, fonts, spacing, shadows } = useTheme();
const zohoPeopleState = useSelector(selectZohoPeopleState);
@ -74,12 +76,20 @@ const ZohoPeopleDashboardScreen: React.FC = () => {
Zoho People Dashboard
</Text>
</View>
<TouchableOpacity
style={[styles.refreshButton, { backgroundColor: colors.primary }]}
onPress={handleRefresh}
>
<Icon name="refresh" size={20} color={colors.surface} />
</TouchableOpacity>
<View style={styles.headerRight}>
<TouchableOpacity
style={[styles.dataButton, { backgroundColor: '#3B82F6', marginRight: 8 }]}
onPress={() => navigation.navigate('ZohoPeopleData' as never)}
>
<Icon name="list" size={20} color={colors.surface} />
</TouchableOpacity>
<TouchableOpacity
style={[styles.refreshButton, { backgroundColor: colors.primary }]}
onPress={handleRefresh}
>
<Icon name="refresh" size={20} color={colors.surface} />
</TouchableOpacity>
</View>
</View>
</View>
@ -442,6 +452,10 @@ const styles = StyleSheet.create({
fontSize: 20,
marginLeft: 8,
},
headerRight: {
flexDirection: 'row',
alignItems: 'center',
},
refreshButton: {
width: 36,
height: 36,
@ -449,6 +463,13 @@ const styles = StyleSheet.create({
justifyContent: 'center',
alignItems: 'center',
},
dataButton: {
width: 36,
height: 36,
borderRadius: 18,
justifyContent: 'center',
alignItems: 'center',
},
content: {
flex: 1,
},

View File

@ -0,0 +1,607 @@
import React, { useState, useEffect, useCallback } from 'react';
import {
View,
Text,
StyleSheet,
ScrollView,
TouchableOpacity,
RefreshControl,
FlatList,
} from 'react-native';
import { useSelector, useDispatch } from 'react-redux';
import type { AppDispatch } from '@/store/store';
import Icon from 'react-native-vector-icons/MaterialIcons';
import { Container, LoadingSpinner, ErrorState } from '@/shared/components/ui';
import { useTheme } from '@/shared/styles/useTheme';
import { fetchZohoPeopleData } from '../store/hrSlice';
import {
selectZohoPeopleLoading,
selectZohoPeopleError,
selectAttendanceReport,
selectEmployeeForms,
selectHolidays,
selectLeaveData,
} from '../store/selectors';
const ZohoPeopleDataScreen: React.FC = () => {
const { colors, fonts, spacing, shadows } = useTheme();
const dispatch = useDispatch<AppDispatch>();
const [selectedTab, setSelectedTab] = useState<'employees' | 'attendance' | 'leaves' | 'holidays'>('employees');
const [refreshing, setRefreshing] = useState(false);
// Redux selectors
const attendanceReport = useSelector(selectAttendanceReport);
const employeeForms = useSelector(selectEmployeeForms);
const holidays = useSelector(selectHolidays);
const leaveData = useSelector(selectLeaveData);
const loading = useSelector(selectZohoPeopleLoading);
const error = useSelector(selectZohoPeopleError);
// Fetch data using Redux
const fetchData = useCallback(async (showRefresh = false) => {
try {
if (showRefresh) {
setRefreshing(true);
}
await dispatch(fetchZohoPeopleData()).unwrap();
} catch (err) {
console.error('Error fetching Zoho People data:', err);
} finally {
setRefreshing(false);
}
}, [dispatch]);
useEffect(() => {
fetchData();
}, [fetchData]);
const handleRefresh = useCallback(() => {
fetchData(true);
}, [fetchData]);
const handleRetry = useCallback(() => {
fetchData();
}, [fetchData]);
// Loading state
if (loading && !employeeForms?.length && !attendanceReport?.length) {
return <LoadingSpinner />;
}
// Error state
if (error) {
return <ErrorState onRetry={handleRetry} />;
}
// Tab configuration
const tabs = [
{ key: 'employees', label: 'Employees', icon: 'people', count: employeeForms?.length || 0 },
{ key: 'attendance', label: 'Attendance', icon: 'schedule', count: attendanceReport?.length || 0 },
{ key: 'leaves', label: 'Leaves', icon: 'event-available', count: leaveData?.length || 0 },
{ key: 'holidays', label: 'Holidays', icon: 'event', count: holidays?.length || 0 },
] as const;
const renderEmployeeCard = useCallback((item: any) => (
<View style={[styles.card, { backgroundColor: colors.surface, borderColor: colors.border }]}>
<View style={styles.cardHeader}>
<View style={styles.cardTitleContainer}>
<Icon name="person" size={20} color={colors.primary} />
<Text style={[styles.cardTitle, { color: colors.text, fontFamily: fonts.bold }]}>
{item.FirstName} {item.LastName}
</Text>
</View>
<View style={[styles.statusBadge, { backgroundColor: item.Employeestatus === 'Active' ? '#E9FAF2' : '#FEF2F2' }]}>
<Text style={[styles.statusText, { color: item.Employeestatus === 'Active' ? '#10B981' : '#EF4444', fontFamily: fonts.medium }]}>
{item.Employeestatus}
</Text>
</View>
</View>
<View style={styles.cardContent}>
<View style={styles.infoRow}>
<Text style={[styles.infoLabel, { color: colors.textLight, fontFamily: fonts.regular }]}>Employee ID:</Text>
<Text style={[styles.infoValue, { color: colors.text, fontFamily: fonts.medium }]}>
{item.EmployeeID}
</Text>
</View>
<View style={styles.infoRow}>
<Text style={[styles.infoLabel, { color: colors.textLight, fontFamily: fonts.regular }]}>Department:</Text>
<Text style={[styles.infoValue, { color: colors.text, fontFamily: fonts.medium }]}>
{item.Department || 'N/A'}
</Text>
</View>
<View style={styles.infoRow}>
<Text style={[styles.infoLabel, { color: colors.textLight, fontFamily: fonts.regular }]}>Role:</Text>
<Text style={[styles.infoValue, { color: colors.text, fontFamily: fonts.medium }]}>
{item.Role || 'N/A'}
</Text>
</View>
{item.EmailID && (
<View style={styles.infoRow}>
<Text style={[styles.infoLabel, { color: colors.textLight, fontFamily: fonts.regular }]}>Email:</Text>
<Text style={[styles.infoValue, { color: colors.text, fontFamily: fonts.medium }]}>{item.EmailID}</Text>
</View>
)}
<View style={styles.infoRow}>
<Text style={[styles.infoLabel, { color: colors.textLight, fontFamily: fonts.regular }]}>Experience:</Text>
<Text style={[styles.infoValue, { color: colors.text, fontFamily: fonts.medium }]}>
{item.Experience || 'N/A'} years
</Text>
</View>
<View style={styles.infoRow}>
<Text style={[styles.infoLabel, { color: colors.textLight, fontFamily: fonts.regular }]}>Date of Joining:</Text>
<Text style={[styles.infoValue, { color: colors.text, fontFamily: fonts.medium }]}>
{item.Dateofjoining || 'N/A'}
</Text>
</View>
</View>
</View>
), [colors, fonts]);
const renderAttendanceCard = useCallback((item: any) => {
const today = new Date();
const todayISO = today.toISOString().split('T')[0];
const todayFormatted = todayISO.split('-').reverse().join('-');
const todayAttendance = item.attendanceDetails?.[todayFormatted] || item.attendanceDetails?.[todayISO];
return (
<View style={[styles.card, { backgroundColor: colors.surface, borderColor: colors.border }]}>
<View style={styles.cardHeader}>
<View style={styles.cardTitleContainer}>
<Icon name="schedule" size={20} color={colors.primary} />
<Text style={[styles.cardTitle, { color: colors.text, fontFamily: fonts.bold }]}>
{item.employeeDetails['first name']} {item.employeeDetails['last name']}
</Text>
</View>
<View style={[styles.statusBadge, { backgroundColor: getAttendanceStatusColor(todayAttendance?.Status) }]}>
<Text style={[styles.statusText, { color: colors.text, fontFamily: fonts.medium }]}>
{todayAttendance?.Status || 'No Data'}
</Text>
</View>
</View>
<View style={styles.cardContent}>
<View style={styles.infoRow}>
<Text style={[styles.infoLabel, { color: colors.textLight, fontFamily: fonts.regular }]}>Employee ID:</Text>
<Text style={[styles.infoValue, { color: colors.text, fontFamily: fonts.medium }]}>
{item.employeeDetails.erecno}
</Text>
</View>
{todayAttendance && (
<>
<View style={styles.infoRow}>
<Text style={[styles.infoLabel, { color: colors.textLight, fontFamily: fonts.regular }]}>First In:</Text>
<Text style={[styles.infoValue, { color: colors.text, fontFamily: fonts.medium }]}>
{todayAttendance.FirstIn || 'N/A'}
</Text>
</View>
<View style={styles.infoRow}>
<Text style={[styles.infoLabel, { color: colors.textLight, fontFamily: fonts.regular }]}>Last Out:</Text>
<Text style={[styles.infoValue, { color: colors.text, fontFamily: fonts.medium }]}>
{todayAttendance.LastOut || 'N/A'}
</Text>
</View>
<View style={styles.amountRow}>
<View style={styles.amountItem}>
<Text style={[styles.amountLabel, { color: colors.textLight, fontFamily: fonts.regular }]}>Working Hours:</Text>
<Text style={[styles.amountValue, { color: colors.primary, fontFamily: fonts.bold }]}>
{todayAttendance.WorkingHours || '00:00'}
</Text>
</View>
<View style={styles.amountItem}>
<Text style={[styles.amountLabel, { color: colors.textLight, fontFamily: fonts.regular }]}>Total Hours:</Text>
<Text style={[styles.amountValue, { color: '#10B981', fontFamily: fonts.bold }]}>
{todayAttendance.TotalHours || '00:00'}
</Text>
</View>
</View>
</>
)}
</View>
</View>
);
}, [colors, fonts]);
const renderLeaveCard = useCallback((item: any) => (
<View style={[styles.card, { backgroundColor: colors.surface, borderColor: colors.border }]}>
<View style={styles.cardHeader}>
<View style={styles.cardTitleContainer}>
<Icon name="event-available" size={20} color={colors.primary} />
<Text style={[styles.cardTitle, { color: colors.text, fontFamily: fonts.bold }]}>
{item.Employee_ID}
</Text>
</View>
<View style={[styles.statusBadge, { backgroundColor: getLeaveStatusColor(item.ApprovalStatus) }]}>
<Text style={[styles.statusText, { color: colors.text, fontFamily: fonts.medium }]}>
{item.ApprovalStatus}
</Text>
</View>
</View>
<View style={styles.cardContent}>
<View style={styles.infoRow}>
<Text style={[styles.infoLabel, { color: colors.textLight, fontFamily: fonts.regular }]}>Leave Type:</Text>
<Text style={[styles.infoValue, { color: colors.text, fontFamily: fonts.medium }]}>
{item.Leavetype}
</Text>
</View>
<View style={styles.infoRow}>
<Text style={[styles.infoLabel, { color: colors.textLight, fontFamily: fonts.regular }]}>From:</Text>
<Text style={[styles.infoValue, { color: colors.text, fontFamily: fonts.medium }]}>
{item.From || 'N/A'}
</Text>
</View>
<View style={styles.infoRow}>
<Text style={[styles.infoLabel, { color: colors.textLight, fontFamily: fonts.regular }]}>To:</Text>
<Text style={[styles.infoValue, { color: colors.text, fontFamily: fonts.medium }]}>
{item.To || 'N/A'}
</Text>
</View>
<View style={styles.infoRow}>
<Text style={[styles.infoLabel, { color: colors.textLight, fontFamily: fonts.regular }]}>Days Taken:</Text>
<Text style={[styles.infoValue, { color: colors.text, fontFamily: fonts.medium }]}>
{item.Daystaken} days
</Text>
</View>
{item.Reasonforleave && (
<View style={styles.infoRow}>
<Text style={[styles.infoLabel, { color: colors.textLight, fontFamily: fonts.regular }]}>Reason:</Text>
<Text style={[styles.infoValue, { color: colors.text, fontFamily: fonts.medium }]}>
{item.Reasonforleave}
</Text>
</View>
)}
</View>
</View>
), [colors, fonts]);
const renderHolidayCard = useCallback((item: any) => (
<View style={[styles.card, { backgroundColor: colors.surface, borderColor: colors.border }]}>
<View style={styles.cardHeader}>
<View style={styles.cardTitleContainer}>
<Icon name="event" size={20} color={colors.primary} />
<Text style={[styles.cardTitle, { color: colors.text, fontFamily: fonts.bold }]}>
{item.Name}
</Text>
</View>
<View style={[styles.statusBadge, { backgroundColor: getHolidayTypeColor(item) }]}>
<Text style={[styles.statusText, { color: colors.text, fontFamily: fonts.medium }]}>
{item.isRestrictedHoliday ? 'Restricted' : item.isHalfday ? 'Half Day' : 'Full Day'}
</Text>
</View>
</View>
<View style={styles.cardContent}>
<View style={styles.infoRow}>
<Text style={[styles.infoLabel, { color: colors.textLight, fontFamily: fonts.regular }]}>Date:</Text>
<Text style={[styles.infoValue, { color: colors.text, fontFamily: fonts.medium }]}>
{item.Date || 'N/A'}
</Text>
</View>
<View style={styles.infoRow}>
<Text style={[styles.infoLabel, { color: colors.textLight, fontFamily: fonts.regular }]}>Location:</Text>
<Text style={[styles.infoValue, { color: colors.text, fontFamily: fonts.medium }]}>
{item.LocationName || 'All Locations'}
</Text>
</View>
<View style={styles.infoRow}>
<Text style={[styles.infoLabel, { color: colors.textLight, fontFamily: fonts.regular }]}>Shift:</Text>
<Text style={[styles.infoValue, { color: colors.text, fontFamily: fonts.medium }]}>
{item.ShiftName || 'N/A'}
</Text>
</View>
{item.Remarks && (
<View style={styles.infoRow}>
<Text style={[styles.infoLabel, { color: colors.textLight, fontFamily: fonts.regular }]}>Remarks:</Text>
<Text style={[styles.infoValue, { color: colors.text, fontFamily: fonts.medium }]}>
{item.Remarks}
</Text>
</View>
)}
</View>
</View>
), [colors, fonts]);
const renderTabContent = useCallback(() => {
const commonFlatListProps = {
showsVerticalScrollIndicator: false,
contentContainerStyle: styles.listContainer,
refreshControl: (
<RefreshControl refreshing={refreshing} onRefresh={handleRefresh} />
),
removeClippedSubviews: true,
maxToRenderPerBatch: 10,
windowSize: 10,
initialNumToRender: 10,
};
switch (selectedTab) {
case 'employees':
return (
<FlatList
data={employeeForms || []}
renderItem={({ item }) => renderEmployeeCard(item)}
keyExtractor={(_, index) => `emp-${index}`}
{...commonFlatListProps}
/>
);
case 'attendance':
return (
<FlatList
data={attendanceReport || []}
renderItem={({ item }) => renderAttendanceCard(item)}
keyExtractor={(_, index) => `att-${index}`}
{...commonFlatListProps}
/>
);
case 'leaves':
return (
<FlatList
data={leaveData || []}
renderItem={({ item }) => renderLeaveCard(item)}
keyExtractor={(_, index) => `leave-${index}`}
{...commonFlatListProps}
/>
);
case 'holidays':
return (
<FlatList
data={holidays || []}
renderItem={({ item }) => renderHolidayCard(item)}
keyExtractor={(_, index) => `hol-${index}`}
{...commonFlatListProps}
/>
);
default:
return null;
}
}, [selectedTab, employeeForms, attendanceReport, leaveData, holidays, refreshing, handleRefresh, renderEmployeeCard, renderAttendanceCard, renderLeaveCard, renderHolidayCard]);
return (
<View style={[styles.container, { backgroundColor: colors.background }]}>
{/* Fixed Header */}
<View style={styles.header}>
<Text style={[styles.title, { color: colors.text, fontFamily: fonts.bold }]}>
Zoho People Data
</Text>
<TouchableOpacity onPress={handleRefresh} disabled={refreshing}>
<Icon name="refresh" size={24} color={colors.primary} />
</TouchableOpacity>
</View>
{/* Fixed Tabs */}
<View style={styles.tabsContainer}>
<ScrollView horizontal showsHorizontalScrollIndicator={false}>
<View style={styles.tabs}>
{tabs.map((tab) => (
<TouchableOpacity
key={tab.key}
style={[
styles.tab,
selectedTab === tab.key && { backgroundColor: colors.primary },
]}
onPress={() => setSelectedTab(tab.key)}
activeOpacity={0.8}
>
<Icon
name={tab.icon}
size={20}
color={selectedTab === tab.key ? colors.surface : colors.textLight}
/>
<Text
style={[
styles.tabText,
{
color: selectedTab === tab.key ? colors.surface : colors.textLight,
fontFamily: fonts.medium,
},
]}
>
{tab.label}
</Text>
<View
style={[
styles.countBadge,
{ backgroundColor: selectedTab === tab.key ? colors.surface : colors.primary },
]}
>
<Text
style={[
styles.countText,
{
color: selectedTab === tab.key ? colors.primary : colors.surface,
fontFamily: fonts.bold,
},
]}
>
{tab.count}
</Text>
</View>
</TouchableOpacity>
))}
</View>
</ScrollView>
</View>
{/* Scrollable Content */}
<View style={styles.content}>
{renderTabContent()}
</View>
</View>
);
};
// Helper functions to get status colors
const getAttendanceStatusColor = (status: string): string => {
const statusColors: Record<string, string> = {
present: '#E9FAF2',
absent: '#FEF2F2',
weekend: '#F3F4F6',
'': '#F3F4F6',
};
return statusColors[status?.toLowerCase()] || '#F3F4F6';
};
const getLeaveStatusColor = (status: string): string => {
const statusColors: Record<string, string> = {
approved: '#E9FAF2',
pending: '#FEF3C7',
rejected: '#FEF2F2',
draft: '#F3F4F6',
};
return statusColors[status?.toLowerCase()] || '#F3F4F6';
};
const getHolidayTypeColor = (holiday: any): string => {
if (holiday.isRestrictedHoliday) return '#FEF3C7';
if (holiday.isHalfday) return '#E0F2FE';
return '#E9FAF2';
};
const styles = StyleSheet.create({
container: {
flex: 1,
},
header: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
paddingHorizontal: 16,
paddingVertical: 12,
},
title: {
fontSize: 24,
},
tabsContainer: {
backgroundColor: '#FFFFFF',
paddingVertical: 12,
borderBottomWidth: 1,
borderBottomColor: '#E5E7EB',
},
tabs: {
flexDirection: 'row',
paddingHorizontal: 16,
},
tab: {
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: 16,
paddingVertical: 10,
marginRight: 8,
borderRadius: 20,
backgroundColor: '#F1F5F9',
},
tabText: {
marginLeft: 6,
fontSize: 14,
},
countBadge: {
marginLeft: 6,
paddingHorizontal: 8,
paddingVertical: 2,
borderRadius: 10,
minWidth: 20,
alignItems: 'center',
},
countText: {
fontSize: 12,
},
content: {
flex: 1,
},
listContainer: {
paddingHorizontal: 16,
paddingBottom: 20,
},
card: {
borderRadius: 12,
borderWidth: 1,
padding: 16,
marginBottom: 12,
...StyleSheet.flatten({
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 3,
}),
},
cardHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 12,
},
cardTitleContainer: {
flexDirection: 'row',
alignItems: 'center',
flex: 1,
},
cardTitle: {
fontSize: 16,
marginLeft: 8,
},
statusBadge: {
paddingHorizontal: 8,
paddingVertical: 4,
borderRadius: 12,
},
statusText: {
fontSize: 12,
textTransform: 'capitalize',
},
cardContent: {
gap: 8,
},
infoRow: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
},
infoLabel: {
fontSize: 14,
flex: 1,
},
infoValue: {
fontSize: 14,
textAlign: 'right',
flex: 1,
},
amountRow: {
flexDirection: 'row',
justifyContent: 'space-between',
marginTop: 8,
paddingTop: 8,
borderTopWidth: StyleSheet.hairlineWidth,
borderTopColor: '#E5E7EB',
},
amountItem: {
flex: 1,
alignItems: 'center',
},
amountLabel: {
fontSize: 12,
marginBottom: 4,
},
amountValue: {
fontSize: 16,
fontWeight: 'bold',
},
});
export default ZohoPeopleDataScreen;

View File

@ -1,5 +1,5 @@
import React from 'react';
import { View, Text, StyleSheet, FlatList, TouchableOpacity } from 'react-native';
import { View, Text, StyleSheet, FlatList, TouchableOpacity, Alert } from 'react-native';
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
import { useTheme } from '@/shared/styles/useTheme';
import type { RouteProp } from '@react-navigation/native';
@ -9,6 +9,7 @@ import { setSelectedService } from '@/modules/integrations/store/integrationsSli
import type { AppDispatch } from '@/store/store';
import ZohoAuth from './ZohoAuth';
import { Modal } from 'react-native';
import httpClient from '@/services/http';
type Route = RouteProp<IntegrationsStackParamList, 'IntegrationCategory'>;
@ -59,8 +60,35 @@ const IntegrationCategoryScreen: React.FC<Props> = ({ route }) => {
const dispatch = useDispatch<AppDispatch>();
const [showZohoAuth, setShowZohoAuth] = React.useState(false);
const [pendingService, setPendingService] = React.useState<string | null>(null);
const [isCheckingToken, setIsCheckingToken] = React.useState(false);
const services = servicesMap[route.params.categoryKey] ?? [];
// Check for existing Zoho token
const checkZohoToken = async (serviceKey: string) => {
try {
setIsCheckingToken(true);
const response = await httpClient.get('/api/v1/users/decrypt-token?service_name=zoho');
const responseData = response.data as any;
if (responseData.status === 'success' && responseData.data?.accessToken) {
// Token exists and is valid, navigate directly
console.log('Zoho token found, navigating directly to:', serviceKey);
dispatch(setSelectedService(serviceKey));
} else {
// No valid token, show auth modal
setPendingService(serviceKey);
setShowZohoAuth(true);
}
} catch (error) {
console.log('No valid Zoho token found, showing auth modal');
// Token doesn't exist or is invalid, show auth modal
setPendingService(serviceKey);
setShowZohoAuth(true);
} finally {
setIsCheckingToken(false);
}
};
return (
<View style={[styles.container, { backgroundColor: colors.background }]}>
<FlatList
@ -70,15 +98,16 @@ const IntegrationCategoryScreen: React.FC<Props> = ({ route }) => {
ItemSeparatorComponent={() => <View style={[styles.sep, { backgroundColor: colors.border }]} />}
renderItem={({ item }) => (
<TouchableOpacity
style={styles.row}
style={[styles.row, isCheckingToken && styles.disabledRow]}
activeOpacity={0.8}
disabled={isCheckingToken}
onPress={() => {
// Here we decide whether to require Zoho authentication first
const requiresZohoAuth = item.key === 'zohoProjects' || item.key === 'zohoPeople' || item.key === 'zohoBooks' || item.key === 'zohoCRM';
console.log('key pressed',item.key)
console.log('key pressed', item.key);
if (requiresZohoAuth) {
setPendingService(item.key);
setShowZohoAuth(true);
checkZohoToken(item.key);
} else {
dispatch(setSelectedService(item.key));
}
@ -88,6 +117,13 @@ const IntegrationCategoryScreen: React.FC<Props> = ({ route }) => {
<Icon name={item.icon} size={20} color={colors.primary} />
</View>
<Text style={[styles.title, { color: colors.text, fontFamily: fonts.medium }]}>{item.title}</Text>
{isCheckingToken && (
<View style={styles.loadingContainer}>
<Text style={[styles.loadingText, { color: colors.textLight, fontFamily: fonts.regular }]}>
Checking...
</Text>
</View>
)}
</TouchableOpacity>
)}
/>
@ -131,6 +167,9 @@ const styles = StyleSheet.create({
alignItems: 'center',
paddingVertical: 12,
},
disabledRow: {
opacity: 0.6,
},
iconCircle: {
width: 36,
height: 36,
@ -139,7 +178,17 @@ const styles = StyleSheet.create({
justifyContent: 'center',
marginRight: 12,
},
title: { fontSize: 14 },
title: {
fontSize: 14,
flex: 1,
},
loadingContainer: {
marginLeft: 8,
},
loadingText: {
fontSize: 12,
fontStyle: 'italic',
},
});
export default IntegrationCategoryScreen;