diff --git a/src/assets/assets.ts b/src/assets/assets.ts new file mode 100644 index 0000000..2e8023a --- /dev/null +++ b/src/assets/assets.ts @@ -0,0 +1,30 @@ +// Image Assets +export const Images = { + coming_soon: require('./images/coming_soon.png'), + // Add more images here as needed + // logo: require('./images/logo.png'), + // background: require('./images/background.jpg'), +} as const; + +// Font Assets (if you have custom fonts) +export const Fonts = { + // Add font paths here if needed + // robotoRegular: require('./fonts/Roboto-Regular.ttf'), + // robotoBold: require('./fonts/Roboto-Bold.ttf'), +} as const; + +// Animation Assets (if you have Lottie animations) +export const Animations = { + // Add animation paths here if needed + // loading: require('./animations/loading.json'), + // success: require('./animations/success.json'), +} as const; + +// Export all assets +export const Assets = { + Images, + Fonts, + Animations, +} as const; + +export default Assets; diff --git a/src/assets/images/coming_soon.png b/src/assets/images/coming_soon.png new file mode 100644 index 0000000..77a9757 Binary files /dev/null and b/src/assets/images/coming_soon.png differ diff --git a/src/modules/hr/zoho/store/selectors.ts b/src/modules/hr/zoho/store/selectors.ts index c27b248..00a6740 100644 --- a/src/modules/hr/zoho/store/selectors.ts +++ b/src/modules/hr/zoho/store/selectors.ts @@ -39,7 +39,6 @@ export const selectAttendanceAnalytics = createSelector( const today = new Date(); const todayISO = today.toISOString().split('T')[0]; // YYYY-MM-DD const todayFormatted = todayISO.split('-').reverse().join('-'); // DD-MM-YYYY - const todayAlternative = todayISO; // YYYY-MM-DD (in case API uses this format) let presentToday = 0; let absentToday = 0; @@ -47,28 +46,36 @@ export const selectAttendanceAnalytics = createSelector( let totalWorkingHours = 0; let employeesWithHours = 0; + console.log('Today ISO:', todayISO); + console.log('Today Formatted:', todayFormatted); + attendanceReport.forEach((employee) => { if (!employee.attendanceDetails) { return; } - // Try different date formats + // Check all available date formats for today const todayAttendance = employee.attendanceDetails[todayFormatted] || - employee.attendanceDetails[todayAlternative] || - employee.attendanceDetails[todayISO]; + employee.attendanceDetails[todayISO] || + employee.attendanceDetails[todayISO.split('-').reverse().join('-')]; + + console.log('Employee attendance keys:', Object.keys(employee.attendanceDetails)); + console.log('Today attendance found:', !!todayAttendance); if (todayAttendance) { - const status = todayAttendance.Status; + const status = todayAttendance.Status?.toLowerCase() || ''; + console.log('Today status for employee:', status); - if (status === 'Present' || status === '' || status === 'present') { + if (status === 'present') { presentToday++; - } else if (status === 'Absent' || status === 'absent') { + } else if (status === 'absent' || status === '') { + // Empty status means absent as per Indian shift time (09:00 AM) absentToday++; - } else if (status === 'Weekend' || status === 'weekend') { + } else if (status === 'weekend') { weekendToday++; } - // Calculate working hours + // Calculate working hours for today const workingHours = todayAttendance.WorkingHours || todayAttendance.TotalHours; if (workingHours && workingHours !== '00:00' && workingHours !== '0:00') { try { @@ -81,6 +88,9 @@ export const selectAttendanceAnalytics = createSelector( // Silently handle parsing errors } } + } else { + // If no attendance data for today, count as absent + absentToday++; } }); @@ -88,6 +98,8 @@ export const selectAttendanceAnalytics = createSelector( const averageWorkingHours = employeesWithHours > 0 ? totalWorkingHours / employeesWithHours : 0; const attendanceRate = totalEmployees > 0 ? (presentToday / totalEmployees) * 100 : 0; + console.log('Final counts - Present:', presentToday, 'Absent:', absentToday, 'Weekend:', weekendToday); + return { totalEmployees, presentToday, @@ -197,10 +209,68 @@ export const selectHolidayAnalytics = createSelector( if (!holidays || holidays.length === 0) return null; const today = new Date(); + today.setHours(0, 0, 0, 0); // Reset time to start of day for accurate comparison + + console.log('Today date for holiday comparison:', today); + console.log('All holidays:', holidays.map(h => ({ name: h.Name, date: h.Date }))); + const upcomingHolidays = holidays.filter(holiday => { - const holidayDate = new Date(holiday.Date); + // Try different date parsing methods + let holidayDate; + + // Method 1: Direct parsing (works for DD-MMM-YYYY format like "02-Oct-2025") + holidayDate = new Date(holiday.Date); + + // Method 2: If invalid, try parsing DD-MM-YYYY format + if (isNaN(holidayDate.getTime())) { + const parts = holiday.Date.split('-'); + if (parts.length === 3) { + holidayDate = new Date(parseInt(parts[2]), parseInt(parts[1]) - 1, parseInt(parts[0])); + } + } + + // Method 3: Try YYYY-MM-DD format + if (isNaN(holidayDate.getTime())) { + const parts = holiday.Date.split('-'); + if (parts.length === 3) { + holidayDate = new Date(parseInt(parts[0]), parseInt(parts[1]) - 1, parseInt(parts[2])); + } + } + + // Method 4: Try DD-MMM-YYYY format manually (e.g., "02-Oct-2025") + if (isNaN(holidayDate.getTime())) { + const parts = holiday.Date.split('-'); + if (parts.length === 3) { + const day = parseInt(parts[0]); + const monthStr = parts[1]; + const year = parseInt(parts[2]); + + // Map month names to numbers + const monthMap: { [key: string]: number } = { + 'Jan': 0, 'Feb': 1, 'Mar': 2, 'Apr': 3, 'May': 4, 'Jun': 5, + 'Jul': 6, 'Aug': 7, 'Sep': 8, 'Oct': 9, 'Nov': 10, 'Dec': 11 + }; + + const month = monthMap[monthStr]; + if (month !== undefined) { + holidayDate = new Date(year, month, day); + } + } + } + + holidayDate.setHours(0, 0, 0, 0); // Reset time to start of day + + console.log(`Holiday: ${holiday.Name}, Date: ${holiday.Date}, Parsed: ${holidayDate}, Is upcoming: ${holidayDate >= today}`); + return holidayDate >= today; - }).sort((a, b) => new Date(a.Date).getTime() - new Date(b.Date).getTime()); + }).sort((a, b) => { + // Use the same parsing logic for sorting + const dateA = new Date(a.Date); + const dateB = new Date(b.Date); + return dateA.getTime() - dateB.getTime(); + }); + + console.log('Upcoming holidays found:', upcomingHolidays.length); const totalHolidays = holidays.length; const restrictedHolidays = holidays.filter(h => h.isRestrictedHoliday).length; diff --git a/src/modules/integrations/navigation/IntegrationsNavigator.tsx b/src/modules/integrations/navigation/IntegrationsNavigator.tsx index fafc6e7..69fdf04 100644 --- a/src/modules/integrations/navigation/IntegrationsNavigator.tsx +++ b/src/modules/integrations/navigation/IntegrationsNavigator.tsx @@ -2,10 +2,12 @@ import React from 'react'; import { createStackNavigator } from '@react-navigation/stack'; import IntegrationsHomeScreen from '../screens/IntegrationsHomeScreen'; import IntegrationCategoryScreen from '../screens/IntegrationCategoryScreen'; +import ComingSoonScreen from '../screens/ComingSoonScreen'; export type IntegrationsStackParamList = { IntegrationsHome: undefined; IntegrationCategory: { categoryKey: string; title: string }; + ComingSoon: undefined; }; const Stack = createStackNavigator(); @@ -14,6 +16,7 @@ const IntegrationsNavigator = () => ( ({ title: route.params.title,headerShown:false })} /> + ); diff --git a/src/modules/integrations/screens/ComingSoonScreen.tsx b/src/modules/integrations/screens/ComingSoonScreen.tsx new file mode 100644 index 0000000..aa74af1 --- /dev/null +++ b/src/modules/integrations/screens/ComingSoonScreen.tsx @@ -0,0 +1,196 @@ +import React from 'react'; +import { + View, + Text, + StyleSheet, + Image, + TouchableOpacity, + SafeAreaView, +} from 'react-native'; +import Icon from 'react-native-vector-icons/MaterialIcons'; +import { useTheme } from '@/shared/styles/useTheme'; +import { useNavigation } from '@react-navigation/native'; +import { Images } from '@/assets/assets'; + +const ComingSoonScreen: React.FC = () => { + const { colors, fonts, spacing, shadows } = useTheme(); + const navigation = useNavigation(); + + const handleGoBack = () => { + navigation.goBack(); + }; + + return ( + + {/* Header with Home Icon */} + + + + + + + {/* Main Content */} + + {/* Coming Soon Image */} + + + + + {/* Text Content */} + + + New Experience Arriving Shortly + + + + We're working hard to bring you an amazing experience. This feature will be available soon! + + + + + + + Enhanced Dashboard + + + + + + + Real-time Analytics + + + + + + + Advanced Reporting + + + + + + Stay tuned for updates! We'll notify you as soon as this feature is ready. + + + + + {/* Footer */} + + + + + Back to Categories + + + + + ); +}; + +const styles = StyleSheet.create({ + container: { + flex: 1, + }, + header: { + flexDirection: 'row', + justifyContent: 'flex-end', + alignItems: 'center', + paddingHorizontal: 16, + paddingVertical: 12, + }, + homeButton: { + width: 48, + height: 48, + borderRadius: 24, + justifyContent: 'center', + alignItems: 'center', + ...StyleSheet.flatten({ + shadowColor: '#000', + shadowOffset: { width: 0, height: 2 }, + shadowOpacity: 0.15, + shadowRadius: 4, + elevation: 4, + }), + }, + content: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + paddingHorizontal: 24, + }, + imageContainer: { + marginBottom: 32, + alignItems: 'center', + }, + comingSoonImage: { + width: 280, + height: 280, + }, + textContainer: { + alignItems: 'center', + maxWidth: 320, + }, + title: { + fontSize: 28, + textAlign: 'center', + marginBottom: 16, + lineHeight: 36, + }, + subtitle: { + fontSize: 16, + textAlign: 'center', + marginBottom: 32, + lineHeight: 24, + }, + featureList: { + marginBottom: 24, + width: '100%', + }, + featureItem: { + flexDirection: 'row', + alignItems: 'center', + marginBottom: 12, + }, + featureText: { + fontSize: 14, + marginLeft: 12, + }, + note: { + fontSize: 14, + textAlign: 'center', + fontStyle: 'italic', + lineHeight: 20, + }, + footer: { + paddingHorizontal: 24, + paddingBottom: 24, + }, + backButton: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + paddingVertical: 16, + paddingHorizontal: 24, + borderRadius: 12, + borderWidth: 1, + }, + backButtonText: { + fontSize: 16, + marginLeft: 8, + }, +}); + +export default ComingSoonScreen; diff --git a/src/modules/integrations/screens/IntegrationCategoryScreen.tsx b/src/modules/integrations/screens/IntegrationCategoryScreen.tsx index 28bd3e1..e3e7c0a 100644 --- a/src/modules/integrations/screens/IntegrationCategoryScreen.tsx +++ b/src/modules/integrations/screens/IntegrationCategoryScreen.tsx @@ -10,6 +10,7 @@ import type { AppDispatch } from '@/store/store'; import ZohoAuth from './ZohoAuth'; import { Modal } from 'react-native'; import httpClient from '@/services/http'; +import { useNavigation } from '@react-navigation/native'; type Route = RouteProp; @@ -58,6 +59,7 @@ interface Props { const IntegrationCategoryScreen: React.FC = ({ route }) => { const { colors, fonts } = useTheme(); const dispatch = useDispatch(); + const navigation = useNavigation(); const [showZohoAuth, setShowZohoAuth] = React.useState(false); const [pendingService, setPendingService] = React.useState(null); const [isCheckingToken, setIsCheckingToken] = React.useState(false); @@ -109,7 +111,8 @@ const IntegrationCategoryScreen: React.FC = ({ route }) => { if (requiresZohoAuth) { checkZohoToken(item.key); } else { - dispatch(setSelectedService(item.key)); + // For non-Zoho services, navigate to Coming Soon screen + navigation.navigate('ComingSoon' as never); } }} > diff --git a/src/services/http.ts b/src/services/http.ts index bb5e50b..b3df1cb 100644 --- a/src/services/http.ts +++ b/src/services/http.ts @@ -8,7 +8,7 @@ import { clearSelectedService } from '@/modules/integrations/store/integrationsS let pendingRequest: any = null; const http = create({ - baseURL: 'http://192.168.1.19:4000', + baseURL: 'http://192.168.1.17:4000', // baseURL: 'http://160.187.167.216', timeout: 10000, });