coming soon screen added
This commit is contained in:
parent
e479b59ae3
commit
e91bbbbd1d
30
src/assets/assets.ts
Normal file
30
src/assets/assets.ts
Normal file
@ -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;
|
||||||
BIN
src/assets/images/coming_soon.png
Normal file
BIN
src/assets/images/coming_soon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 890 KiB |
@ -39,7 +39,6 @@ export const selectAttendanceAnalytics = createSelector(
|
|||||||
const today = new Date();
|
const today = new Date();
|
||||||
const todayISO = today.toISOString().split('T')[0]; // YYYY-MM-DD
|
const todayISO = today.toISOString().split('T')[0]; // YYYY-MM-DD
|
||||||
const todayFormatted = todayISO.split('-').reverse().join('-'); // DD-MM-YYYY
|
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 presentToday = 0;
|
||||||
let absentToday = 0;
|
let absentToday = 0;
|
||||||
@ -47,28 +46,36 @@ export const selectAttendanceAnalytics = createSelector(
|
|||||||
let totalWorkingHours = 0;
|
let totalWorkingHours = 0;
|
||||||
let employeesWithHours = 0;
|
let employeesWithHours = 0;
|
||||||
|
|
||||||
|
console.log('Today ISO:', todayISO);
|
||||||
|
console.log('Today Formatted:', todayFormatted);
|
||||||
|
|
||||||
attendanceReport.forEach((employee) => {
|
attendanceReport.forEach((employee) => {
|
||||||
if (!employee.attendanceDetails) {
|
if (!employee.attendanceDetails) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try different date formats
|
// Check all available date formats for today
|
||||||
const todayAttendance = employee.attendanceDetails[todayFormatted] ||
|
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) {
|
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++;
|
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++;
|
absentToday++;
|
||||||
} else if (status === 'Weekend' || status === 'weekend') {
|
} else if (status === 'weekend') {
|
||||||
weekendToday++;
|
weekendToday++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate working hours
|
// Calculate working hours for today
|
||||||
const workingHours = todayAttendance.WorkingHours || todayAttendance.TotalHours;
|
const workingHours = todayAttendance.WorkingHours || todayAttendance.TotalHours;
|
||||||
if (workingHours && workingHours !== '00:00' && workingHours !== '0:00') {
|
if (workingHours && workingHours !== '00:00' && workingHours !== '0:00') {
|
||||||
try {
|
try {
|
||||||
@ -81,6 +88,9 @@ export const selectAttendanceAnalytics = createSelector(
|
|||||||
// Silently handle parsing errors
|
// 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 averageWorkingHours = employeesWithHours > 0 ? totalWorkingHours / employeesWithHours : 0;
|
||||||
const attendanceRate = totalEmployees > 0 ? (presentToday / totalEmployees) * 100 : 0;
|
const attendanceRate = totalEmployees > 0 ? (presentToday / totalEmployees) * 100 : 0;
|
||||||
|
|
||||||
|
console.log('Final counts - Present:', presentToday, 'Absent:', absentToday, 'Weekend:', weekendToday);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
totalEmployees,
|
totalEmployees,
|
||||||
presentToday,
|
presentToday,
|
||||||
@ -197,10 +209,68 @@ export const selectHolidayAnalytics = createSelector(
|
|||||||
if (!holidays || holidays.length === 0) return null;
|
if (!holidays || holidays.length === 0) return null;
|
||||||
|
|
||||||
const today = new Date();
|
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 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;
|
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 totalHolidays = holidays.length;
|
||||||
const restrictedHolidays = holidays.filter(h => h.isRestrictedHoliday).length;
|
const restrictedHolidays = holidays.filter(h => h.isRestrictedHoliday).length;
|
||||||
|
|||||||
@ -2,10 +2,12 @@ import React from 'react';
|
|||||||
import { createStackNavigator } from '@react-navigation/stack';
|
import { createStackNavigator } from '@react-navigation/stack';
|
||||||
import IntegrationsHomeScreen from '../screens/IntegrationsHomeScreen';
|
import IntegrationsHomeScreen from '../screens/IntegrationsHomeScreen';
|
||||||
import IntegrationCategoryScreen from '../screens/IntegrationCategoryScreen';
|
import IntegrationCategoryScreen from '../screens/IntegrationCategoryScreen';
|
||||||
|
import ComingSoonScreen from '../screens/ComingSoonScreen';
|
||||||
|
|
||||||
export type IntegrationsStackParamList = {
|
export type IntegrationsStackParamList = {
|
||||||
IntegrationsHome: undefined;
|
IntegrationsHome: undefined;
|
||||||
IntegrationCategory: { categoryKey: string; title: string };
|
IntegrationCategory: { categoryKey: string; title: string };
|
||||||
|
ComingSoon: undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Stack = createStackNavigator<IntegrationsStackParamList>();
|
const Stack = createStackNavigator<IntegrationsStackParamList>();
|
||||||
@ -14,6 +16,7 @@ const IntegrationsNavigator = () => (
|
|||||||
<Stack.Navigator>
|
<Stack.Navigator>
|
||||||
<Stack.Screen name="IntegrationsHome" component={IntegrationsHomeScreen} options={{ title: 'Integrations',headerShown:false }} />
|
<Stack.Screen name="IntegrationsHome" component={IntegrationsHomeScreen} options={{ title: 'Integrations',headerShown:false }} />
|
||||||
<Stack.Screen name="IntegrationCategory" component={IntegrationCategoryScreen} options={({ route }) => ({ title: route.params.title,headerShown:false })} />
|
<Stack.Screen name="IntegrationCategory" component={IntegrationCategoryScreen} options={({ route }) => ({ title: route.params.title,headerShown:false })} />
|
||||||
|
<Stack.Screen name="ComingSoon" component={ComingSoonScreen} options={{ title: 'Coming Soon', headerShown: false }} />
|
||||||
</Stack.Navigator>
|
</Stack.Navigator>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
196
src/modules/integrations/screens/ComingSoonScreen.tsx
Normal file
196
src/modules/integrations/screens/ComingSoonScreen.tsx
Normal file
@ -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 (
|
||||||
|
<SafeAreaView style={[styles.container, { backgroundColor: colors.background }]}>
|
||||||
|
{/* Header with Home Icon */}
|
||||||
|
<View style={styles.header}>
|
||||||
|
<TouchableOpacity
|
||||||
|
style={[styles.homeButton, { backgroundColor: colors.primary }]}
|
||||||
|
onPress={handleGoBack}
|
||||||
|
activeOpacity={0.8}
|
||||||
|
>
|
||||||
|
<Icon name="home" size={24} color={colors.surface} />
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* Main Content */}
|
||||||
|
<View style={styles.content}>
|
||||||
|
{/* Coming Soon Image */}
|
||||||
|
<View style={styles.imageContainer}>
|
||||||
|
<Image
|
||||||
|
source={Images.coming_soon}
|
||||||
|
style={styles.comingSoonImage}
|
||||||
|
resizeMode="contain"
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* Text Content */}
|
||||||
|
<View style={styles.textContainer}>
|
||||||
|
<Text style={[styles.title, { color: colors.text, fontFamily: fonts.bold }]}>
|
||||||
|
New Experience Arriving Shortly
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<Text style={[styles.subtitle, { color: colors.textLight, fontFamily: fonts.regular }]}>
|
||||||
|
We're working hard to bring you an amazing experience. This feature will be available soon!
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<View style={styles.featureList}>
|
||||||
|
<View style={styles.featureItem}>
|
||||||
|
<Icon name="check-circle" size={20} color={colors.primary} />
|
||||||
|
<Text style={[styles.featureText, { color: colors.text, fontFamily: fonts.regular }]}>
|
||||||
|
Enhanced Dashboard
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View style={styles.featureItem}>
|
||||||
|
<Icon name="check-circle" size={20} color={colors.primary} />
|
||||||
|
<Text style={[styles.featureText, { color: colors.text, fontFamily: fonts.regular }]}>
|
||||||
|
Real-time Analytics
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View style={styles.featureItem}>
|
||||||
|
<Icon name="check-circle" size={20} color={colors.primary} />
|
||||||
|
<Text style={[styles.featureText, { color: colors.text, fontFamily: fonts.regular }]}>
|
||||||
|
Advanced Reporting
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<Text style={[styles.note, { color: colors.textLight, fontFamily: fonts.regular }]}>
|
||||||
|
Stay tuned for updates! We'll notify you as soon as this feature is ready.
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* Footer */}
|
||||||
|
<View style={styles.footer}>
|
||||||
|
<TouchableOpacity
|
||||||
|
style={[styles.backButton, { backgroundColor: colors.primary, borderColor: colors.primary }]}
|
||||||
|
onPress={handleGoBack}
|
||||||
|
activeOpacity={0.8}
|
||||||
|
>
|
||||||
|
<Icon name="arrow-back" size={20} color={colors.surface} />
|
||||||
|
<Text style={[styles.backButtonText, { color: colors.surface, fontFamily: fonts.medium }]}>
|
||||||
|
Back to Categories
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
</SafeAreaView>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
@ -10,6 +10,7 @@ import type { AppDispatch } from '@/store/store';
|
|||||||
import ZohoAuth from './ZohoAuth';
|
import ZohoAuth from './ZohoAuth';
|
||||||
import { Modal } from 'react-native';
|
import { Modal } from 'react-native';
|
||||||
import httpClient from '@/services/http';
|
import httpClient from '@/services/http';
|
||||||
|
import { useNavigation } from '@react-navigation/native';
|
||||||
|
|
||||||
type Route = RouteProp<IntegrationsStackParamList, 'IntegrationCategory'>;
|
type Route = RouteProp<IntegrationsStackParamList, 'IntegrationCategory'>;
|
||||||
|
|
||||||
@ -58,6 +59,7 @@ interface Props {
|
|||||||
const IntegrationCategoryScreen: React.FC<Props> = ({ route }) => {
|
const IntegrationCategoryScreen: React.FC<Props> = ({ route }) => {
|
||||||
const { colors, fonts } = useTheme();
|
const { colors, fonts } = useTheme();
|
||||||
const dispatch = useDispatch<AppDispatch>();
|
const dispatch = useDispatch<AppDispatch>();
|
||||||
|
const navigation = useNavigation();
|
||||||
const [showZohoAuth, setShowZohoAuth] = React.useState(false);
|
const [showZohoAuth, setShowZohoAuth] = React.useState(false);
|
||||||
const [pendingService, setPendingService] = React.useState<string | null>(null);
|
const [pendingService, setPendingService] = React.useState<string | null>(null);
|
||||||
const [isCheckingToken, setIsCheckingToken] = React.useState(false);
|
const [isCheckingToken, setIsCheckingToken] = React.useState(false);
|
||||||
@ -109,7 +111,8 @@ const IntegrationCategoryScreen: React.FC<Props> = ({ route }) => {
|
|||||||
if (requiresZohoAuth) {
|
if (requiresZohoAuth) {
|
||||||
checkZohoToken(item.key);
|
checkZohoToken(item.key);
|
||||||
} else {
|
} else {
|
||||||
dispatch(setSelectedService(item.key));
|
// For non-Zoho services, navigate to Coming Soon screen
|
||||||
|
navigation.navigate('ComingSoon' as never);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import { clearSelectedService } from '@/modules/integrations/store/integrationsS
|
|||||||
let pendingRequest: any = null;
|
let pendingRequest: any = null;
|
||||||
|
|
||||||
const http = create({
|
const http = create({
|
||||||
baseURL: 'http://192.168.1.19:4000',
|
baseURL: 'http://192.168.1.17:4000',
|
||||||
// baseURL: 'http://160.187.167.216',
|
// baseURL: 'http://160.187.167.216',
|
||||||
timeout: 10000,
|
timeout: 10000,
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user