Animation case detail || Aman Singh (#61)
* case details, animation and data validations * changed linting issue * changed submodule path
This commit is contained in:
committed by
GitHub Enterprise
parent
cb660f888b
commit
6463c24ffa
@@ -1,25 +1,25 @@
|
||||
import crashlytics from '@react-native-firebase/crashlytics';
|
||||
import { RouteProp } from '@react-navigation/native';
|
||||
import { createNativeStackNavigator } from '@react-navigation/native-stack';
|
||||
import React from 'react';
|
||||
import { getUniqueId } from 'react-native-device-info';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { _map } from './RN-UI-LIB/src/utlis/common';
|
||||
import { GenericType } from './src/common/GenericTypes';
|
||||
import Widget from './src/components/form';
|
||||
import { setGlobalUserData } from './src/constants/Global';
|
||||
import RealTemplate from './src/data/RealTemplateData.json';
|
||||
import { useAppDispatch } from './src/hooks';
|
||||
import useFirestoreUpdates from './src/hooks/useFirestoreUpdates';
|
||||
import { setDeviceId } from './src/reducer/userSlice';
|
||||
import AllCasesMain from './src/screens/allCases';
|
||||
import CaseDetails from './src/screens/caseDetails/CaseDetails';
|
||||
import interactionsHandler from './src/screens/caseDetails/interactionsHandler';
|
||||
import Login from './src/screens/login';
|
||||
import OtpInput from './src/screens/login/OtpInput';
|
||||
import Profile from './src/screens/Profile';
|
||||
import TodoList from './src/screens/todoList/TodoList';
|
||||
import { RootState } from './src/store/store';
|
||||
import Profile from './src/screens/Profile';
|
||||
import interactionsHandler from './src/screens/caseDetails/interactionsHandler';
|
||||
import useFirestoreUpdates from './src/hooks/useFirestoreUpdates';
|
||||
import crashlytics from '@react-native-firebase/crashlytics';
|
||||
import { RouteProp } from '@react-navigation/native';
|
||||
import { GenericType } from './src/common/GenericTypes';
|
||||
|
||||
const ANIMATION_DURATION = 300;
|
||||
|
||||
@@ -80,7 +80,7 @@ const ProtectedRouter = () => {
|
||||
options={{
|
||||
header: () => null,
|
||||
animationDuration: ANIMATION_DURATION,
|
||||
animation: 'slide_from_right',
|
||||
animation: 'none',
|
||||
}}
|
||||
listeners={getScreenFocusListenerObj}
|
||||
/>
|
||||
|
||||
Submodule RN-UI-LIB updated: f536a952ae...6aaa1d0174
@@ -37,6 +37,7 @@ export const getAllCases =
|
||||
|
||||
export const getAllCaseDetails =
|
||||
(data: Array<ICaseItem>) => (dispatch: AppDispatch) => {
|
||||
if(!data) return;
|
||||
const caseList = data.map(caseItem => caseItem?.caseReferenceId);
|
||||
const url = getApiUrl(ApiKeys.CASE_DETAIL);
|
||||
axiosInstance
|
||||
|
||||
@@ -127,7 +127,7 @@ axiosInstance.interceptors.response.use(
|
||||
) {
|
||||
const errorString = getErrorMessage(error);
|
||||
if (!config.headers.donotHandleError) {
|
||||
toast({ type: 'error', text1: JSON.stringify(errorString) });
|
||||
toast({ type: 'error', text1:errorString || "Something went wrong" });
|
||||
}
|
||||
|
||||
if (response.status === 401) {
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
import { getCurrentScreen, navigateToScreen } from '../components/utlis/navigationUtlis';
|
||||
import { CurrentTask, ICaseItem, IFilter } from '../screens/allCases/interface';
|
||||
import {
|
||||
FirestoreUpdateTypes,
|
||||
} from '../common/Constants';
|
||||
import { CaseDetail } from '../screens/caseDetails/interface';
|
||||
import { CaseUpdates } from '../hooks/useFirestoreUpdates';
|
||||
import { updateRecordsOnCustomerDbFromCaseList } from '../components/utlis/customerDbHelper';
|
||||
import { _map } from '../../RN-UI-LIB/src/utlis/common';
|
||||
import {
|
||||
FirestoreUpdateTypes
|
||||
} from '../common/Constants';
|
||||
import { updateRecordsOnCustomerDbFromCaseList } from '../components/utlis/customerDbHelper';
|
||||
import { getCurrentScreen, navigateToScreen } from '../components/utlis/navigationUtlis';
|
||||
import { CaseUpdates } from '../hooks/useFirestoreUpdates';
|
||||
import { CurrentTask, ICaseItem, IFilter } from '../screens/allCases/interface';
|
||||
import { CaseDetail } from '../screens/caseDetails/interface';
|
||||
|
||||
export type ICasesMap = { [key: string]: ICaseItem };
|
||||
|
||||
@@ -80,6 +80,7 @@ const allCasesSlice = createSlice({
|
||||
},
|
||||
setCasesListData: (state, action) => {
|
||||
const { allCases } = action.payload;
|
||||
if(!allCases) return;
|
||||
// TODO add type
|
||||
const listData: Array<any> = [];
|
||||
if (allCases?.length) {
|
||||
|
||||
@@ -24,6 +24,7 @@ interface ICaseReducer {
|
||||
toBeSynced: any;
|
||||
allCases: Array<Data>;
|
||||
templateData: any;
|
||||
showAlternateText: string;
|
||||
}
|
||||
|
||||
const initialState = {
|
||||
@@ -32,6 +33,7 @@ const initialState = {
|
||||
toBeSynced: {},
|
||||
allCases: [],
|
||||
templateData: {},
|
||||
showAlternateText: ""
|
||||
} as ICaseReducer;
|
||||
|
||||
export const caseSlice = createSlice({
|
||||
@@ -77,22 +79,18 @@ export const caseSlice = createSlice({
|
||||
updateTemplateData: (state, action) => {
|
||||
state.templateData = action.payload;
|
||||
},
|
||||
setAllCases: (state, action) => {
|
||||
state.allCases = action.payload;
|
||||
},
|
||||
updateDate: (state, action) => {
|
||||
const { date } = action.payload;
|
||||
state.loanInfo.allocationDate = date;
|
||||
updateAlternateHeader: (state, action) => {
|
||||
state.showAlternateText = action.payload;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const {
|
||||
updateInteraction,
|
||||
setAllCases,
|
||||
deleteInteraction,
|
||||
updateTemplateData,
|
||||
deleteJourney,
|
||||
updateAlternateHeader
|
||||
} = caseSlice.actions;
|
||||
|
||||
export default caseSlice.reducer;
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
import React from 'react';
|
||||
import {ICaseItem} from './interface';
|
||||
import RoundCheckIcon from '../../icons/RoundCheckIcon';
|
||||
import {useAppDispatch, useAppSelector} from '../../hooks';
|
||||
import {CaseDetail} from '../caseDetails/interface';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
import Avatar from '../../../RN-UI-LIB/src/components/Avatar';
|
||||
import UnsyncedIcon from '../../../RN-UI-LIB/src/Icons/UnsyncedIcon';
|
||||
import {StyleSheet, View} from 'react-native';
|
||||
import { COLORS } from '../../../RN-UI-LIB/src/styles/colors';
|
||||
import { getSignedURLs } from '../../action/dataActions';
|
||||
import { getImage64FromCaseId } from '../../components/utlis/customerDbHelper';
|
||||
import { useAppDispatch, useAppSelector } from '../../hooks';
|
||||
import RoundCheckIcon from '../../icons/RoundCheckIcon';
|
||||
import { CaseDetail } from '../caseDetails/interface';
|
||||
import { ICaseItem } from './interface';
|
||||
|
||||
interface ICaseItemAvatar {
|
||||
caseSelected?: boolean;
|
||||
caseData: ICaseItem;
|
||||
size?: number;
|
||||
showBorder?: boolean;
|
||||
}
|
||||
|
||||
const MAX_API_CALL = 3;
|
||||
@@ -20,11 +22,12 @@ const MAX_API_CALL = 3;
|
||||
const CaseItemAvatar: React.FC<ICaseItemAvatar> = ({
|
||||
caseSelected = false,
|
||||
caseData,
|
||||
size = 32
|
||||
size = 32,
|
||||
showBorder = false,
|
||||
}) => {
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const {caseReferenceId} = caseData;
|
||||
const { caseReferenceId } = caseData;
|
||||
const caseDetails: CaseDetail = useAppSelector(
|
||||
state => state.allCases.caseDetails?.[caseReferenceId],
|
||||
);
|
||||
@@ -34,50 +37,54 @@ const CaseItemAvatar: React.FC<ICaseItemAvatar> = ({
|
||||
|
||||
const isSynced = caseDetails?.isSynced;
|
||||
|
||||
|
||||
React.useEffect(() => {
|
||||
if (caseDetails?.customerInfo?.imageURL) {
|
||||
(async () => {
|
||||
let image64 = await getImage64FromCaseId(caseDetails?.id);
|
||||
if (image64) { // if image exist in WM db, then setting the base64
|
||||
if (image64) {
|
||||
// if image exist in WM db, then setting the base64
|
||||
setImageUrl(image64);
|
||||
} else {
|
||||
setImageUrl(caseDetails.customerInfo.imageURL)
|
||||
setImageUrl(caseDetails.customerInfo.imageURL);
|
||||
}
|
||||
})();
|
||||
})();
|
||||
}
|
||||
}, [caseDetails?.customerInfo?.imageURL]);
|
||||
|
||||
const onError = async () => {
|
||||
if (apiErrorCount < MAX_API_CALL) {
|
||||
dispatch(getSignedURLs([imageUrl])).then((resp) => {
|
||||
dispatch(getSignedURLs([imageUrl])).then(resp => {
|
||||
if (resp?.[imageUrl]) {
|
||||
setApiErrorCount(apiErrorCount => apiErrorCount+1);
|
||||
setImageUrl(resp[imageUrl])
|
||||
setApiErrorCount(apiErrorCount => apiErrorCount + 1);
|
||||
setImageUrl(resp[imageUrl]);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<View>
|
||||
{
|
||||
!caseSelected ? (
|
||||
<View>
|
||||
<Avatar
|
||||
size={size}
|
||||
name={caseDetails?.customerInfo?.name || caseDetails?.customerInfo?.customerName }
|
||||
dataURI={imageUrl}
|
||||
onErrorFallback={onError}
|
||||
/>
|
||||
{!isSynced ? (
|
||||
<View style={styles.unsyncedIcon}>
|
||||
<UnsyncedIcon />
|
||||
</View>
|
||||
) : null}
|
||||
</View>
|
||||
) : <RoundCheckIcon />
|
||||
}
|
||||
{!caseSelected ? (
|
||||
<View>
|
||||
<Avatar
|
||||
size={size}
|
||||
name={
|
||||
caseDetails?.customerInfo?.name ||
|
||||
caseDetails?.customerInfo?.customerName
|
||||
}
|
||||
dataURI={imageUrl}
|
||||
onErrorFallback={onError}
|
||||
style={[showBorder && styles.border]}
|
||||
/>
|
||||
{!isSynced ? (
|
||||
<View style={styles.unsyncedIcon}>
|
||||
<UnsyncedIcon />
|
||||
</View>
|
||||
) : null}
|
||||
</View>
|
||||
) : (
|
||||
<RoundCheckIcon />
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
@@ -88,6 +95,10 @@ const styles = StyleSheet.create({
|
||||
left: 14,
|
||||
top: 14,
|
||||
},
|
||||
border: {
|
||||
borderColor: COLORS.BORDER.BLUE,
|
||||
borderWidth: 2
|
||||
},
|
||||
});
|
||||
|
||||
export default CaseItemAvatar;
|
||||
|
||||
64
src/screens/caseDetails/CaseDetailHeader.tsx
Normal file
64
src/screens/caseDetails/CaseDetailHeader.tsx
Normal file
@@ -0,0 +1,64 @@
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { Animated, StyleSheet } from 'react-native';
|
||||
import Avatar from '../../../RN-UI-LIB/src/components/Avatar';
|
||||
import Heading from '../../../RN-UI-LIB/src/components/Heading';
|
||||
import NavigationHeader from '../../../RN-UI-LIB/src/components/NavigationHeader';
|
||||
import { GenericStyles } from '../../../RN-UI-LIB/src/styles';
|
||||
import { goBack } from '../../components/utlis/navigationUtlis';
|
||||
import { useAppSelector } from '../../hooks';
|
||||
import { CaseDetail } from './interface';
|
||||
|
||||
const AlternateHeader: React.FC<{ imageUrl: string; name: string }> = props => {
|
||||
const { name, imageUrl } = props;
|
||||
const slideInUpAnimation = useRef(new Animated.Value(10)).current;
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
Animated.timing(slideInUpAnimation, {
|
||||
toValue: 0,
|
||||
duration: 300,
|
||||
useNativeDriver: true
|
||||
}).start()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Animated.View style={[GenericStyles.row, {transform: [{translateY : slideInUpAnimation}]}]}>
|
||||
<Avatar name={name} dataURI={imageUrl} />
|
||||
<Heading
|
||||
type={'h4'}
|
||||
bold
|
||||
style={[
|
||||
GenericStyles.whiteText,
|
||||
GenericStyles.ml8,
|
||||
{ alignItems: 'center', marginTop: 2 },
|
||||
]}>
|
||||
{name}
|
||||
</Heading>
|
||||
</Animated.View>
|
||||
);
|
||||
};
|
||||
|
||||
const CaseDetailHeader: React.FC<{ caseDetail: CaseDetail }> = props => {
|
||||
const { caseDetail } = props;
|
||||
|
||||
const showHeader = useAppSelector(state => state.case.showAlternateText);
|
||||
return (
|
||||
<NavigationHeader
|
||||
title={
|
||||
showHeader.length ? (
|
||||
<AlternateHeader
|
||||
name={caseDetail?.customerInfo?.customerName}
|
||||
imageUrl={caseDetail?.customerInfo?.imageURL}
|
||||
/>
|
||||
) : (
|
||||
''
|
||||
)
|
||||
}
|
||||
onBack={goBack}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default CaseDetailHeader;
|
||||
|
||||
const styles = StyleSheet.create({});
|
||||
@@ -1,22 +1,27 @@
|
||||
import React from 'react';
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import {
|
||||
Animated,
|
||||
RefreshControl,
|
||||
SafeAreaView,
|
||||
ScrollView,
|
||||
StatusBar,
|
||||
StyleSheet,
|
||||
View,
|
||||
View
|
||||
} from 'react-native';
|
||||
import Heading from '../../../RN-UI-LIB/src/components/Heading';
|
||||
import NavigationHeader from '../../../RN-UI-LIB/src/components/NavigationHeader';
|
||||
import LineLoader from '../../../RN-UI-LIB/src/components/suspense_loader/LineLoader';
|
||||
import Button from '../../../RN-UI-LIB/src/components/Button';
|
||||
import SuspenseLoader from '../../../RN-UI-LIB/src/components/suspense_loader/SuspenseLoader';
|
||||
import {GenericStyles, SCREEN_WIDTH} from '../../../RN-UI-LIB/src/styles';
|
||||
import {COLORS} from '../../../RN-UI-LIB/src/styles/colors';
|
||||
import {
|
||||
GenericStyles,
|
||||
getShadowStyle, SCREEN_WIDTH
|
||||
} from '../../../RN-UI-LIB/src/styles';
|
||||
import { COLORS } from '../../../RN-UI-LIB/src/styles/colors';
|
||||
import { getSingleCaseDetail } from '../../action/dataActions';
|
||||
import { navigateToScreen } from '../../components/utlis/navigationUtlis';
|
||||
import {useAppDispatch, useAppSelector} from '../../hooks';
|
||||
import { useAppDispatch, useAppSelector } from '../../hooks';
|
||||
import useRefresh from '../../hooks/useRefresh';
|
||||
import CaseDetailsHeader from './CaseDetailsHeader';
|
||||
import { updateAlternateHeader } from '../../reducer/caseReducre';
|
||||
import { CaseStatuses } from '../allCases/interface';
|
||||
import CaseDetailsHeader from './CaseDetailHeader';
|
||||
import CallingBottomSheet from './journeyStepper/CallingBottomSheet';
|
||||
import TaskStepper from './journeyStepper/TaskStepper';
|
||||
import TaskLoader from './TaskLoader';
|
||||
import UserDetailsSection from './UserDetailsSection';
|
||||
@@ -31,39 +36,118 @@ interface ICaseDetails {
|
||||
const CaseDetails: React.FC<ICaseDetails> = props => {
|
||||
const {
|
||||
route: {
|
||||
params: {caseId},
|
||||
params: { caseId },
|
||||
},
|
||||
} = props;
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
const detailObject = useAppSelector(
|
||||
const caseDetail = useAppSelector(
|
||||
state => state.allCases.caseDetails[caseId],
|
||||
);
|
||||
const [showCallingBottomSheet, setShowCallingBottomSheet] =
|
||||
React.useState(false);
|
||||
const offset = useRef(new Animated.Value(0)).current;
|
||||
const [showHeader, setShowHeader] = React.useState(false);
|
||||
const { refreshing, onRefresh } = useRefresh(() =>
|
||||
dispatch(getSingleCaseDetail([caseId])),
|
||||
);
|
||||
|
||||
const handleCustomerCall = () => {
|
||||
// if(mobileNumbers?.length > 0) {
|
||||
// setShowPhoneNumberBottomSheet(true)
|
||||
// return;
|
||||
// }
|
||||
// setSelectedPhoneNumber(mobileNumbers[0]);
|
||||
setShowCallingBottomSheet(true);
|
||||
};
|
||||
|
||||
const opacityAnimation = useRef(new Animated.Value(0)).current;
|
||||
|
||||
const isCaseClosedOrCompleted =
|
||||
caseDetail.caseStatus === CaseStatuses.CLOSED ||
|
||||
caseDetail.caseStatus === CaseStatuses.FORCE_CLOSE ||
|
||||
caseDetail.caseStatus === CaseStatuses.EXPIRED;
|
||||
|
||||
const duration = 600;
|
||||
|
||||
const slideInUpJourney = new Animated.Value(1000);
|
||||
|
||||
useEffect(() => {
|
||||
Animated.timing(slideInUpJourney, {
|
||||
toValue: -58,
|
||||
duration: duration,
|
||||
useNativeDriver: true,
|
||||
}).start();
|
||||
|
||||
Animated.timing(opacityAnimation, {
|
||||
toValue: 1,
|
||||
duration: duration,
|
||||
useNativeDriver: true,
|
||||
}).start();
|
||||
}, []);
|
||||
|
||||
const {refreshing, onRefresh} = useRefresh(() => dispatch(getSingleCaseDetail([caseId])));
|
||||
|
||||
return (
|
||||
<SafeAreaView
|
||||
style={[GenericStyles.fill, GenericStyles.whiteBackground]}>
|
||||
<StatusBar backgroundColor={COLORS.BACKGROUND.HEADER} />
|
||||
<ScrollView
|
||||
stickyHeaderIndices={[0]}
|
||||
onScroll={event => {
|
||||
const scrolling = event.nativeEvent.contentOffset.y;
|
||||
if (scrolling > 19) {
|
||||
dispatch(updateAlternateHeader(caseDetail?.customerInfo?.customerName));
|
||||
} else {
|
||||
dispatch(updateAlternateHeader(""));
|
||||
}
|
||||
}}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={refreshing}
|
||||
onRefresh={onRefresh}
|
||||
/>
|
||||
}>
|
||||
<NavigationHeader title={'Case Detail'} onBack={()=> navigateToScreen("Home")} />
|
||||
{/* <CaseDetailsHeader /> */}
|
||||
<View style={GenericStyles.p16}>
|
||||
<UserDetailsSection caseDetail={detailObject} />
|
||||
<CaseDetailsHeader caseDetail={caseDetail} />
|
||||
<UserDetailsSection caseDetail={caseDetail} />
|
||||
<Animated.View
|
||||
style={[
|
||||
GenericStyles.whiteBackground,
|
||||
{
|
||||
width: SCREEN_WIDTH * 0.93,
|
||||
alignSelf: 'center',
|
||||
borderRadius: 16,
|
||||
},
|
||||
getShadowStyle(2),
|
||||
{ opacity: opacityAnimation },
|
||||
{ transform: [{ translateY: slideInUpJourney }] },
|
||||
]}>
|
||||
<SuspenseLoader
|
||||
loading={!detailObject.tasks}
|
||||
loading={!caseDetail.tasks}
|
||||
fallBack={<TaskLoader />}>
|
||||
<TaskStepper caseDetail={detailObject} />
|
||||
<TaskStepper caseDetail={caseDetail} />
|
||||
</SuspenseLoader>
|
||||
</View>
|
||||
</Animated.View>
|
||||
|
||||
{!isCaseClosedOrCompleted && (
|
||||
<View
|
||||
style={[
|
||||
GenericStyles.row,
|
||||
styles.journeyContainer,
|
||||
]}>
|
||||
<Button
|
||||
style={[styles.callCustomerButton]}
|
||||
title="Call customer"
|
||||
variant="secondary"
|
||||
onPress={handleCustomerCall}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
</ScrollView>
|
||||
<CallingBottomSheet
|
||||
setShowCallingBottomSheet={setShowCallingBottomSheet}
|
||||
showCallingBottomSheet={showCallingBottomSheet}
|
||||
selectedPhoneNumber={caseDetail.customerInfo.primaryPhoneNumber}
|
||||
/>
|
||||
</SafeAreaView>
|
||||
);
|
||||
};
|
||||
@@ -72,6 +156,15 @@ export const styles = StyleSheet.create({
|
||||
navigationContainer: {
|
||||
height: 48,
|
||||
},
|
||||
callCustomerButton: {
|
||||
width: '100%',
|
||||
marginVertical: 10,
|
||||
},
|
||||
journeyContainer: {
|
||||
borderTopWidth: 1,
|
||||
borderTopColor: COLORS.BORDER.PRIMARY,
|
||||
borderStyle: 'dashed',
|
||||
},
|
||||
});
|
||||
|
||||
export default CaseDetails;
|
||||
|
||||
@@ -1,32 +1,28 @@
|
||||
import React, {useCallback, useState} from 'react';
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import {
|
||||
Animated,
|
||||
Image,
|
||||
Modal,
|
||||
SafeAreaView,
|
||||
StyleSheet,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
View
|
||||
} from 'react-native';
|
||||
import Avatar from '../../../RN-UI-LIB/src/components/Avatar';
|
||||
import Heading from '../../../RN-UI-LIB/src/components/Heading';
|
||||
import LineLoader from '../../../RN-UI-LIB/src/components/suspense_loader/LineLoader';
|
||||
import SuspenseLoader from '../../../RN-UI-LIB/src/components/suspense_loader/SuspenseLoader';
|
||||
import Text from '../../../RN-UI-LIB/src/components/Text';
|
||||
import CloseIcon from '../../../RN-UI-LIB/src/Icons/CloseIcon';
|
||||
import UnsyncedIcon from '../../../RN-UI-LIB/src/Icons/UnsyncedIcon';
|
||||
import {GenericStyles} from '../../../RN-UI-LIB/src/styles';
|
||||
import {COLORS} from '../../../RN-UI-LIB/src/styles/colors';
|
||||
import {dateFormat} from '../../../RN-UI-LIB/src/utlis/dates';
|
||||
import CalenderIcon from '../../assets/icons/CalenderIcon';
|
||||
import DocumentIcon from '../../assets/icons/DocumentIcon';
|
||||
import IconLabel from '../../common/IconLabel';
|
||||
import {
|
||||
GenericStyles
|
||||
} from '../../../RN-UI-LIB/src/styles';
|
||||
import { COLORS } from '../../../RN-UI-LIB/src/styles/colors';
|
||||
import { dateFormat } from '../../../RN-UI-LIB/src/utlis/dates';
|
||||
import { decideLoadingState } from '../../components/utlis/commonFunctions';
|
||||
import CaseItemAvatar from '../allCases/CaseItemAvatar';
|
||||
import { ICaseItem } from '../allCases/interface';
|
||||
import {
|
||||
CaseDetail,
|
||||
LoanAccountStatusUIMapping,
|
||||
LoanTypeUIMapping,
|
||||
CaseDetail, LoanTypeUIMapping
|
||||
} from './interface';
|
||||
|
||||
interface IUserDetailsSection {
|
||||
@@ -34,9 +30,10 @@ interface IUserDetailsSection {
|
||||
}
|
||||
|
||||
const UserDetailsSection: React.FC<IUserDetailsSection> = props => {
|
||||
const {caseDetail = {} as CaseDetail} = props;
|
||||
const {customerInfo, loanDetails} = caseDetail;
|
||||
|
||||
const { caseDetail = {} as CaseDetail } = props;
|
||||
const { customerInfo, loanDetails } = caseDetail;
|
||||
const textAnimationSlideInUp = useRef(new Animated.Value(0)).current;
|
||||
const textAnimationFadeIn = useRef(new Animated.Value(0)).current;
|
||||
const disbursalDate = dateFormat(
|
||||
new Date(loanDetails?.disbursalDate),
|
||||
'DD MMM, YYYY',
|
||||
@@ -45,77 +42,140 @@ const UserDetailsSection: React.FC<IUserDetailsSection> = props => {
|
||||
new Date(caseDetail.allocatedAt),
|
||||
'DD MMM, YYYY',
|
||||
);
|
||||
|
||||
const ANIMATION_DURATION = 500;
|
||||
const [openImage, setOpenImage] = useState<boolean>(false);
|
||||
|
||||
const handleOpenClose = useCallback(() => {
|
||||
setOpenImage(prev => !prev);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
Animated.timing(textAnimationSlideInUp, {
|
||||
toValue: 0,
|
||||
duration: ANIMATION_DURATION,
|
||||
useNativeDriver: true,
|
||||
}).start();
|
||||
Animated.timing(textAnimationFadeIn, {
|
||||
toValue: 1,
|
||||
duration: ANIMATION_DURATION,
|
||||
useNativeDriver: true,
|
||||
}).start();
|
||||
}, []);
|
||||
|
||||
const isSynced = caseDetail.isSynced;
|
||||
|
||||
return (
|
||||
<View style={[GenericStyles.row, GenericStyles.pb16, styles.container, GenericStyles.mt12]}>
|
||||
<SuspenseLoader
|
||||
loading={!(customerInfo?.customerName || customerInfo?.name ) && !customerInfo?.imageURL}
|
||||
fallBack={
|
||||
<LineLoader
|
||||
height={64}
|
||||
width={64}
|
||||
style={[GenericStyles.mb24, {borderRadius: 64 / 2}]}
|
||||
/>
|
||||
}>
|
||||
<TouchableOpacity onPress={handleOpenClose}>
|
||||
<CaseItemAvatar
|
||||
size={64}
|
||||
caseData={{...caseDetail, caseReferenceId: caseDetail?.id} as ICaseItem}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
</SuspenseLoader>
|
||||
<View style={[styles.infoContainer]}>
|
||||
<View style={styles.contentContainer}>
|
||||
<Animated.View
|
||||
style={[
|
||||
GenericStyles.row,
|
||||
GenericStyles.p16,
|
||||
{ paddingTop: 0 },
|
||||
{ transform: [{ translateY: textAnimationSlideInUp }] },
|
||||
{ opacity: textAnimationFadeIn },
|
||||
]}>
|
||||
<SuspenseLoader
|
||||
loading={!(customerInfo?.customerName ||customerInfo?.name)}
|
||||
fallBack={<LineLoader height={12} width={100} />}>
|
||||
<Heading dark type="h3">
|
||||
{customerInfo?.customerName || customerInfo?.name}
|
||||
</Heading>
|
||||
loading={
|
||||
!(customerInfo?.customerName || customerInfo?.name) &&
|
||||
!customerInfo?.imageURL
|
||||
}
|
||||
fallBack={
|
||||
<LineLoader
|
||||
height={64}
|
||||
width={64}
|
||||
style={[
|
||||
GenericStyles.mb24,
|
||||
{ borderRadius: 64 / 2 },
|
||||
]}
|
||||
/>
|
||||
}>
|
||||
<TouchableOpacity onPress={handleOpenClose}>
|
||||
<CaseItemAvatar
|
||||
showBorder
|
||||
size={64}
|
||||
caseData={
|
||||
{
|
||||
...caseDetail,
|
||||
caseReferenceId: caseDetail?.id,
|
||||
} as ICaseItem
|
||||
}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
</SuspenseLoader>
|
||||
<View
|
||||
style={[
|
||||
GenericStyles.row,
|
||||
GenericStyles.mb8,
|
||||
GenericStyles.mt4,
|
||||
]}>
|
||||
<IconLabel
|
||||
icon={<DocumentIcon />}
|
||||
text={LoanTypeUIMapping[loanDetails?.loanType]}
|
||||
/>
|
||||
<IconLabel
|
||||
containerStyle={GenericStyles.ml10}
|
||||
text={
|
||||
LoanAccountStatusUIMapping[
|
||||
loanDetails?.loanAccountStatus
|
||||
]
|
||||
<View style={[styles.infoContainer]}>
|
||||
<SuspenseLoader
|
||||
loading={
|
||||
!(customerInfo?.customerName || customerInfo?.name)
|
||||
}
|
||||
/>
|
||||
fallBack={<LineLoader height={12} width={100} />}>
|
||||
<Heading style={GenericStyles.whiteText} dark type="h3">
|
||||
{customerInfo?.customerName || customerInfo?.name}
|
||||
</Heading>
|
||||
</SuspenseLoader>
|
||||
<View
|
||||
style={[
|
||||
GenericStyles.row,
|
||||
GenericStyles.mb8,
|
||||
GenericStyles.mt4,
|
||||
]}>
|
||||
<Text
|
||||
style={[
|
||||
styles.chip,
|
||||
GenericStyles.whiteBackground,
|
||||
]}>
|
||||
{LoanTypeUIMapping[loanDetails?.loanType]}
|
||||
</Text>
|
||||
<Text
|
||||
style={[
|
||||
styles.chip,
|
||||
GenericStyles.whiteBackground,
|
||||
GenericStyles.ml8,
|
||||
]}>
|
||||
{loanDetails?.tenureMonths}{' '}
|
||||
{loanDetails?.tenureMonths > 1 ? 'Months' : 'Month'}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={GenericStyles.mb8}>
|
||||
<Text style={GenericStyles.whiteText}>
|
||||
Availed on{' '}
|
||||
<Text style={GenericStyles.whiteText} dark bold>
|
||||
{disbursalDate}
|
||||
</Text>
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View style={[GenericStyles.row, GenericStyles.mb8]}>
|
||||
<IconLabel icon={<CalenderIcon />} text={disbursalDate} />
|
||||
<IconLabel
|
||||
text={`${loanDetails?.tenureMonths} ${
|
||||
loanDetails?.tenureMonths > 1 ? 'Months' : 'Month'
|
||||
}`}
|
||||
containerStyle={GenericStyles.ml10}
|
||||
/>
|
||||
</View>
|
||||
<View style={[GenericStyles.pt16]}>
|
||||
<Text light style={[GenericStyles.fontSize15]}>
|
||||
Allocated on <SuspenseLoader loading={decideLoadingState(allocationDate)} fallBack={<LineLoader height={12} width={70} />}>
|
||||
<Text light>{allocationDate}</Text>
|
||||
</Animated.View>
|
||||
<Animated.View
|
||||
style={[
|
||||
GenericStyles.p16,
|
||||
{ paddingTop: 0 },
|
||||
{ transform: [{ translateY: textAnimationSlideInUp }] },
|
||||
{ opacity: textAnimationFadeIn },
|
||||
]}>
|
||||
<View
|
||||
style={{
|
||||
borderTopWidth: 1,
|
||||
borderTopColor: COLORS.BORDER.PRIMARY,
|
||||
}}
|
||||
/>
|
||||
<View style={[GenericStyles.pt16, { alignItems: 'center' }]}>
|
||||
<Text
|
||||
light
|
||||
style={[
|
||||
[GenericStyles.fontSize15, GenericStyles.whiteText],
|
||||
]}>
|
||||
Case allocated on{' '}
|
||||
<SuspenseLoader
|
||||
loading={decideLoadingState(allocationDate)}
|
||||
fallBack={<LineLoader height={12} width={70} />}>
|
||||
<Text bold style={GenericStyles.whiteText} light>
|
||||
{allocationDate}
|
||||
</Text>
|
||||
</SuspenseLoader>
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</Animated.View>
|
||||
|
||||
<Modal
|
||||
animationType={'slide'}
|
||||
visible={openImage}
|
||||
@@ -123,7 +183,7 @@ const UserDetailsSection: React.FC<IUserDetailsSection> = props => {
|
||||
<SafeAreaView
|
||||
style={[
|
||||
GenericStyles.fill,
|
||||
{backgroundColor: COLORS.BACKGROUND.HEADER},
|
||||
{ backgroundColor: COLORS.BACKGROUND.HEADER },
|
||||
]}>
|
||||
<View style={GenericStyles.p16}>
|
||||
<TouchableOpacity onPress={handleOpenClose}>
|
||||
@@ -138,8 +198,8 @@ const UserDetailsSection: React.FC<IUserDetailsSection> = props => {
|
||||
]}>
|
||||
<Image
|
||||
style={[GenericStyles.fill, styles.imageStyle]}
|
||||
source={{uri: customerInfo?.imageURL}}
|
||||
resizeMode={'contain'}
|
||||
source={{ uri: customerInfo?.imageURL }}
|
||||
resizeMode={'stretch'}
|
||||
/>
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
@@ -149,15 +209,11 @@ const UserDetailsSection: React.FC<IUserDetailsSection> = props => {
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: COLORS.BORDER.PRIMARY,
|
||||
},
|
||||
infoContainer: {
|
||||
marginLeft: 16,
|
||||
},
|
||||
imageStyle: {
|
||||
height: 300,
|
||||
height: 500,
|
||||
},
|
||||
backgroundColor: {
|
||||
backgroundColor: COLORS.BACKGROUND.HEADER,
|
||||
@@ -167,6 +223,15 @@ const styles = StyleSheet.create({
|
||||
left: 45,
|
||||
top: 45,
|
||||
},
|
||||
chip: {
|
||||
paddingHorizontal: 8,
|
||||
paddingVertical: 2,
|
||||
borderRadius: 6,
|
||||
},
|
||||
contentContainer: {
|
||||
backgroundColor: COLORS.BACKGROUND.HEADER,
|
||||
paddingBottom: 60,
|
||||
},
|
||||
});
|
||||
|
||||
export default UserDetailsSection;
|
||||
|
||||
@@ -12,6 +12,7 @@ const interactionsHandler = () => {
|
||||
const allCasesDetails = useAppSelector(state => state.allCases.caseDetails);
|
||||
useEffect(() => {
|
||||
console.log("getting called")
|
||||
if(!allCasesDetails) return;
|
||||
let notSyncedCases: Array<any> = [];
|
||||
_map(allCasesDetails, (el)=> {
|
||||
console.log(allCasesDetails[el]?.isSynced === false && allCasesDetails[el].isApiCalled === false)
|
||||
|
||||
@@ -1,19 +1,12 @@
|
||||
import React from 'react';
|
||||
import { PixelRatio, StyleSheet, View } from 'react-native';
|
||||
import Button from '../../../../RN-UI-LIB/src/components/Button';
|
||||
import Heading from '../../../../RN-UI-LIB/src/components/Heading';
|
||||
import { GenericStyles } from '../../../../RN-UI-LIB/src/styles';
|
||||
import { COLORS } from '../../../../RN-UI-LIB/src/styles/colors';
|
||||
import { navigateToScreen } from '../../../components/utlis/navigationUtlis';
|
||||
import {
|
||||
CaseStatuses,
|
||||
CurrentTask,
|
||||
TaskTitleUIMapping,
|
||||
CaseStatuses
|
||||
} from '../../allCases/interface';
|
||||
import { CaseDetail, Context, CONTEXT_TASK_STATUSES, Task } from '../interface';
|
||||
import CallingBottomSheet from './CallingBottomSheet';
|
||||
import StepperHeader, { STEPPER_STATE } from './StepperHeader';
|
||||
import TaskContent from './TaskContent';
|
||||
import { CaseDetail } from '../interface';
|
||||
import ClosedStepperRenderer from "./ClosedStepperRenderer";
|
||||
import OpenStepperRenderer from "./OpenStepperRenderer";
|
||||
|
||||
@@ -47,16 +40,18 @@ const TaskStepper: React.FC<ITaskStepper> = props => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<View style={[GenericStyles.p16]}>
|
||||
<Heading type="h3" style={styles.header} dark bold>
|
||||
<View style={[{paddingBottom: 0}]}>
|
||||
<Heading type="h3" style={[styles.header, GenericStyles.p16]} dark bold>
|
||||
Addresses
|
||||
</Heading>
|
||||
<View style={[GenericStyles.p16, {paddingTop:0, paddingBottom: 0}]}>
|
||||
{
|
||||
isCaseClosedOrCompleted ? <ClosedStepperRenderer tasks={caseDetail.tasks} context={caseDetail.context} currentTask={caseDetail.currentTask} />
|
||||
: <OpenStepperRenderer tasks={caseDetail.tasks} context={caseDetail.context} currentTask={caseDetail.currentTask} caseReferenceId={caseDetail.id} />
|
||||
}
|
||||
</View>
|
||||
</View>
|
||||
{ !isCaseClosedOrCompleted && (
|
||||
{/* { !isCaseClosedOrCompleted && (
|
||||
<View style={[GenericStyles.row, styles.journeyContainer]}>
|
||||
<Button
|
||||
style={[styles.callCustomerButton]}
|
||||
@@ -65,7 +60,7 @@ const TaskStepper: React.FC<ITaskStepper> = props => {
|
||||
onPress={handleCustomerCall}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
)} */}
|
||||
{/* commenting this since we don't have mobile numbers as of right now
|
||||
<PhoneNumberSelectionBottomSheet
|
||||
showPhoneNumberBottomSheet={showPhoneNumberBottomSheet}
|
||||
@@ -75,11 +70,11 @@ const TaskStepper: React.FC<ITaskStepper> = props => {
|
||||
setShowCallingBottomSheet={setShowCallingBottomSheet}
|
||||
setSelectedPhoneNumber={setSelectedPhoneNumber}
|
||||
/>*/}
|
||||
<CallingBottomSheet
|
||||
{/* <CallingBottomSheet
|
||||
setShowCallingBottomSheet={setShowCallingBottomSheet}
|
||||
showCallingBottomSheet={showCallingBottomSheet}
|
||||
selectedPhoneNumber={caseDetail.customerInfo.primaryPhoneNumber}
|
||||
/>
|
||||
/> */}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user