NTP-22476 | Training module integration
This commit is contained in:
34
src/action/TrainingMaterialAction.ts
Normal file
34
src/action/TrainingMaterialAction.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import axiosInstance, { ApiKeys, getApiUrl } from '@components/utlis/apiHelper';
|
||||
import {
|
||||
setTrainingMaterialData,
|
||||
setTrainingMaterialLoading,
|
||||
} from '@reducers/trainingMaterialSlice';
|
||||
import { AppDispatch } from '@store';
|
||||
|
||||
export const getTrainingMaterialList = () => (dispatch: AppDispatch) => {
|
||||
dispatch(setTrainingMaterialLoading(true));
|
||||
const url = getApiUrl(ApiKeys.GET_TRAINING_MATERIAL_LIST);
|
||||
axiosInstance
|
||||
.get(url)
|
||||
.then((res) => {
|
||||
if (res.data) {
|
||||
dispatch(setTrainingMaterialLoading(false));
|
||||
if (res?.data) {
|
||||
dispatch(setTrainingMaterialData(res.data));
|
||||
}
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
dispatch(setTrainingMaterialLoading(false));
|
||||
});
|
||||
};
|
||||
|
||||
export const getTrainingMaterialDetails = async (docRefId: string) => {
|
||||
try {
|
||||
const url = getApiUrl(ApiKeys.GET_TRAINING_MATERIAL_DETAILS, { docRefId });
|
||||
const response = await axiosInstance.get(url);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
@@ -7,6 +7,7 @@ import { IPdfRenderer } from './interfaces';
|
||||
import RNFetchBlob from 'react-native-blob-util';
|
||||
import SuspenseLoader from '@rn-ui-lib/components/suspense_loader/SuspenseLoader';
|
||||
import Text from '@rn-ui-lib/components/Text';
|
||||
import { isFunction } from '@components/utlis/commonFunctions';
|
||||
|
||||
const ERROR_STATE = 'ERROR';
|
||||
|
||||
@@ -38,7 +39,8 @@ const PdfRenderer: React.FC<IPdfRenderer> = ({ docId, url, onPageChange }) => {
|
||||
}, [url]);
|
||||
|
||||
const handlePageChange = (pageNumber: number) => {
|
||||
onPageChange?.(pageNumber);
|
||||
if (isNaN(pageNumber) || !isFunction(onPageChange)) return;
|
||||
onPageChange(pageNumber + 1);
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -112,6 +112,8 @@ export enum ApiKeys {
|
||||
GET_REPAYMENTS = 'GET_REPAYMENTS',
|
||||
GET_FEEDBACK_HISTORY = 'GET_FEEDBACK_HISTORY',
|
||||
GET_PRIORTIY_FEEDBACK = 'GET_PRIORTIY_FEEDBACK',
|
||||
GET_TRAINING_MATERIAL_LIST = 'GET_TRAINING_MATERIAL_LIST',
|
||||
GET_TRAINING_MATERIAL_DETAILS = 'GET_TRAINING_MATERIAL_DETAILS',
|
||||
}
|
||||
|
||||
export const API_URLS: Record<ApiKeys, string> = {} as Record<ApiKeys, string>;
|
||||
@@ -164,8 +166,7 @@ API_URLS[ApiKeys.GET_PERFORMANCE_METRICS] = '/allocation-cycle/agent-performance
|
||||
API_URLS[ApiKeys.GET_CASH_COLLECTED] = '/allocation-cycle/cash-collected-split';
|
||||
API_URLS[ApiKeys.GET_TELEPHONE_NUMBERS] =
|
||||
'/v2/collection-cases/telephones-view/{loanAccountNumber}';
|
||||
API_URLS[ApiKeys.GET_TELEPHONE_NUMBERS_V2] =
|
||||
'/collections/telephones-agent-call-activity-view';
|
||||
API_URLS[ApiKeys.GET_TELEPHONE_NUMBERS_V2] = '/collections/telephones-agent-call-activity-view';
|
||||
API_URLS[ApiKeys.FIRESTORE_INCONSISTENCY_INFO] = '/cases/sync-status';
|
||||
API_URLS[ApiKeys.FIRESTORE_INCONSISTENCY_INFO_V2] = '/cases/v2/sync-status';
|
||||
API_URLS[ApiKeys.GET_CASE_DETAILS_FROM_API] =
|
||||
@@ -208,14 +209,15 @@ API_URLS[ApiKeys.SEND_COMMUNICATION_NAVI_ACCOUNT] = '/navi-communications/{loanA
|
||||
API_URLS[ApiKeys.GENERATE_DYNAMIC_DOCUMENT] = '/documents/generate/{loanAccountNumber}';
|
||||
API_URLS[ApiKeys.ALL_ESCALATIONS] = '/customer-escalation';
|
||||
API_URLS[ApiKeys.DOWNLOAD_LATEST_APP] = 'https://longhorn.navi.com/api/app/download';
|
||||
API_URLS[ApiKeys.GET_UNGROUPED_ADDRESSES] =
|
||||
'/collection-cases/ungrouped/addresses';
|
||||
API_URLS[ApiKeys.GET_UNGROUPED_ADDRESSES] = '/collection-cases/ungrouped/addresses';
|
||||
API_URLS[ApiKeys.GET_GROUPED_ADDRESSES_AND_GEOLOCATIONS] =
|
||||
'/collection-cases/grouped/addresses-geo-locations';
|
||||
API_URLS[ApiKeys.GET_EMI_SCHEDULE] = '/collection-cases/emiSchedule';
|
||||
API_URLS[ApiKeys.GET_REPAYMENTS] = '/collection-cases/repayments';
|
||||
API_URLS[ApiKeys.GET_FEEDBACK_HISTORY] = '/feedback/filters';
|
||||
API_URLS[ApiKeys.GET_PRIORTIY_FEEDBACK] = '/feedback/case-status';
|
||||
API_URLS[ApiKeys.GET_TRAINING_MATERIAL_LIST] = '/training-page/content-list';
|
||||
API_URLS[ApiKeys.GET_TRAINING_MATERIAL_DETAILS] = '/training-page/{docRefId}';
|
||||
|
||||
export const API_STATUS_CODE = {
|
||||
OK: 200,
|
||||
@@ -228,7 +230,7 @@ export const API_STATUS_CODE = {
|
||||
INTERNAL_SERVER_ERROR: 500,
|
||||
TOO_MANY_REQUESTS: 429,
|
||||
GONE: 410,
|
||||
POST_OPERATIVE_HOURS_ACTIVITY: 451
|
||||
POST_OPERATIVE_HOURS_ACTIVITY: 451,
|
||||
};
|
||||
|
||||
export const UNAUTHORIZED_VALUES = [API_STATUS_CODE.UNAUTHORIZED, API_STATUS_CODE.FORBIDDEN];
|
||||
@@ -353,9 +355,9 @@ axiosInstance.interceptors.response.use(
|
||||
);
|
||||
if (
|
||||
(config?.headers?.donotHandleError ||
|
||||
donotHandleErrorOnStatusCode.includes(error?.response?.status)) &&
|
||||
donotHandleErrorOnStatusCode.includes(error?.response?.status)) &&
|
||||
// Logout even donotHandleError is true when status code is 401, 403
|
||||
!config?.headers?.autoLogoutOnUnauthorized
|
||||
!config?.headers?.autoLogoutOnUnauthorized
|
||||
) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
@@ -1,42 +1,14 @@
|
||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
import {
|
||||
ITrainingMaterial,
|
||||
TrainingMaterialContentType,
|
||||
} from '@screens/trainingMaterial/interfaces';
|
||||
import { ITrainingMaterial } from '@screens/trainingMaterial/interfaces';
|
||||
|
||||
interface ITrainingMaterialSlice {
|
||||
loading: boolean;
|
||||
data: ITrainingMaterial[];
|
||||
}
|
||||
|
||||
const MOCK_DATA = [
|
||||
{
|
||||
documentReferenceId: 'doc1',
|
||||
topic: 'If Customer is demanding settlement',
|
||||
contentType: TrainingMaterialContentType.PDF,
|
||||
createdAt: '2021-06-02T00:00:00.000Z',
|
||||
isNewMaterial: true,
|
||||
metadata: {
|
||||
duration: '',
|
||||
pageCount: '10 Pages',
|
||||
},
|
||||
},
|
||||
{
|
||||
documentReferenceId: 'doc2',
|
||||
topic: 'How to fill daily commitment',
|
||||
contentType: TrainingMaterialContentType.VIDEO,
|
||||
createdAt: '2021-06-02T00:00:00.000Z',
|
||||
isNewMaterial: false,
|
||||
metadata: {
|
||||
duration: '10 min',
|
||||
pageCount: '',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const initialState: ITrainingMaterialSlice = {
|
||||
loading: false,
|
||||
data: MOCK_DATA,
|
||||
data: [],
|
||||
};
|
||||
|
||||
const TrainingMaterialSlice = createSlice({
|
||||
|
||||
@@ -11,6 +11,7 @@ import TrainingMaterialListItem from './TrainingMaterialListItem';
|
||||
import { setShouldHideTabBar } from '@reducers/commonSlice';
|
||||
import { addClickstreamEvent } from '@services/clickstreamEventService';
|
||||
import { CLICKSTREAM_EVENT_NAMES } from '@common/Constants';
|
||||
import { getTrainingMaterialList } from '@actions/TrainingMaterialAction';
|
||||
|
||||
const TrainingMaterial = () => {
|
||||
const loading = useAppSelector((state) => state.trainingMaterial.loading);
|
||||
@@ -20,6 +21,7 @@ const TrainingMaterial = () => {
|
||||
useEffect(() => {
|
||||
dispatch(setShouldHideTabBar(true));
|
||||
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_TRAINING_MATERIAL_LIST_SCREEN_LOADED);
|
||||
dispatch(getTrainingMaterialList());
|
||||
return () => {
|
||||
dispatch(setShouldHideTabBar(false));
|
||||
};
|
||||
@@ -39,11 +41,8 @@ const TrainingMaterial = () => {
|
||||
<View style={[GenericStyles.pv16, GenericStyles.fill]}>
|
||||
<ScrollView>
|
||||
<View style={GenericStyles.ph16}>
|
||||
{trainingMaterial.map((item) => (
|
||||
<TrainingMaterialListItem
|
||||
key={item.documentReferenceId}
|
||||
trainingMaterialData={item}
|
||||
/>
|
||||
{trainingMaterial?.map((item) => (
|
||||
<TrainingMaterialListItem key={item.referenceId} trainingMaterialData={item} />
|
||||
))}
|
||||
</View>
|
||||
</ScrollView>
|
||||
|
||||
@@ -3,12 +3,15 @@ import Layout from '@screens/layout/Layout';
|
||||
import React, { useEffect } from 'react';
|
||||
import { ITrainingMaterialDetail, TrainingMaterialContentType } from './interfaces';
|
||||
import { goBack } from '@components/utlis/navigationUtlis';
|
||||
import { View } from 'react-native';
|
||||
import { ActivityIndicator, View } from 'react-native';
|
||||
import { GenericStyles } from '@rn-ui-lib/styles';
|
||||
import WebViewVideoPlayer from '@components/webViewVideoPlayer/WebViewVideoPlayer';
|
||||
import PdfRenderer from '@components/pdfRenderer/PdfRenderer';
|
||||
import { addClickstreamEvent } from '@services/clickstreamEventService';
|
||||
import { CLICKSTREAM_EVENT_NAMES } from '@common/Constants';
|
||||
import { getTrainingMaterialDetails } from '@actions/TrainingMaterialAction';
|
||||
import SuspenseLoader from '@rn-ui-lib/components/suspense_loader/SuspenseLoader';
|
||||
import { COLORS } from '@rn-ui-lib/colors';
|
||||
|
||||
const TrainingMaterialDetail: React.FC<ITrainingMaterialDetail> = (props) => {
|
||||
const {
|
||||
@@ -16,38 +19,55 @@ const TrainingMaterialDetail: React.FC<ITrainingMaterialDetail> = (props) => {
|
||||
params: { trainingMaterialData },
|
||||
},
|
||||
} = props;
|
||||
const { topic, contentType, documentReferenceId } = trainingMaterialData;
|
||||
const isVideo = contentType === TrainingMaterialContentType.VIDEO;
|
||||
const { title, fileType, referenceId } = trainingMaterialData;
|
||||
const isVideo = fileType === TrainingMaterialContentType.VIDEO;
|
||||
const [fileDetails, setFileDetails] = React.useState<any>({});
|
||||
|
||||
const getFileDetails = async () => {
|
||||
try {
|
||||
setFileDetails({ ...fileDetails, loading: true, showError: false });
|
||||
const data = await getTrainingMaterialDetails(referenceId);
|
||||
setFileDetails({ ...data, loading: false });
|
||||
} catch {
|
||||
setFileDetails({ ...fileDetails, loading: false, showError: true });
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_TRAINING_MATERIAL_ITEM_LOADED, {
|
||||
contentType,
|
||||
documentReferenceId,
|
||||
fileType,
|
||||
referenceId,
|
||||
});
|
||||
getFileDetails();
|
||||
}, []);
|
||||
|
||||
const handlePageChange = (pageNumber: number) => {
|
||||
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_TRAINING_MATERIAL_PDF_PAGE_CHANGED, {
|
||||
pageNumber,
|
||||
documentReferenceId,
|
||||
referenceId,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<NavigationHeader title={topic} onBack={goBack} />
|
||||
<NavigationHeader title={title} onBack={goBack} />
|
||||
<View
|
||||
style={[GenericStyles.fill, GenericStyles.whiteBackground, GenericStyles.centerAlignedRow]}
|
||||
>
|
||||
{isVideo ? (
|
||||
<WebViewVideoPlayer url="http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4" />
|
||||
) : (
|
||||
<PdfRenderer
|
||||
docId={documentReferenceId}
|
||||
onPageChange={handlePageChange}
|
||||
url="https://www.adobe.com/support/products/enterprise/knowledgecenter/media/c4611_sample_explain.pdf"
|
||||
/>
|
||||
)}
|
||||
<SuspenseLoader
|
||||
fallBack={<ActivityIndicator size={'large'} color={COLORS.BASE.BLUE} />}
|
||||
loading={fileDetails?.loading}
|
||||
>
|
||||
{isVideo ? (
|
||||
<WebViewVideoPlayer url={fileDetails?.signedUri} />
|
||||
) : (
|
||||
<PdfRenderer
|
||||
docId={fileDetails?.referenceId}
|
||||
onPageChange={handlePageChange}
|
||||
url={fileDetails?.signedUri}
|
||||
/>
|
||||
)}
|
||||
</SuspenseLoader>
|
||||
</View>
|
||||
</Layout>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { ITrainingMaterialListItem } from './interfaces';
|
||||
import { ITrainingMaterialListItem, TrainingMaterialContentType } from './interfaces';
|
||||
import { Pressable, StyleSheet, View } from 'react-native';
|
||||
import { GenericStyles } from '@rn-ui-lib/styles';
|
||||
import { TrainingMaterialContentMap } from './constants';
|
||||
@@ -16,14 +16,14 @@ import { CLICKSTREAM_EVENT_NAMES } from '@common/Constants';
|
||||
const TrainingMaterialListItem: React.FC<ITrainingMaterialListItem> = ({
|
||||
trainingMaterialData,
|
||||
}) => {
|
||||
const { contentType, topic, metadata, createdAt, isNewMaterial, documentReferenceId } =
|
||||
const { fileType, title, metadata, createdAt, isNewMaterial, referenceId } =
|
||||
trainingMaterialData || {};
|
||||
const { icon, interaction, metadataKey } = TrainingMaterialContentMap[contentType] || {};
|
||||
const { icon, interaction, metadataKey } = TrainingMaterialContentMap[fileType] || {};
|
||||
const metadataValue = metadata?.[metadataKey];
|
||||
|
||||
const handleMaterialPress = () => {
|
||||
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_TRAINING_MATERIAL_ITEM_CLICKED, {
|
||||
documentReferenceId,
|
||||
referenceId,
|
||||
});
|
||||
navigateToScreen(ProfileScreenStackEnum.TRAINING_MATERIAL_DETAIL, {
|
||||
trainingMaterialData,
|
||||
@@ -47,24 +47,25 @@ const TrainingMaterialListItem: React.FC<ITrainingMaterialListItem> = ({
|
||||
<View style={[GenericStyles.row, GenericStyles.fill]}>
|
||||
{icon}
|
||||
<View style={GenericStyles.ml8}>
|
||||
<Text dark style={[GenericStyles.lh18]}>
|
||||
{topic}
|
||||
<Text dark style={styles.lh14}>
|
||||
{title}
|
||||
</Text>
|
||||
<View style={GenericStyles.row}>
|
||||
<Text small light>
|
||||
{interaction}
|
||||
<Text small>{interaction}</Text>
|
||||
<Text style={styles.bullet} small>
|
||||
{' '}
|
||||
●{' '}
|
||||
</Text>
|
||||
<Text small>
|
||||
{fileType === TrainingMaterialContentType.PDF
|
||||
? `${metadataValue} pages`
|
||||
: metadataValue}
|
||||
</Text>
|
||||
<Text style={styles.bullet} small>
|
||||
{' '}
|
||||
●{' '}
|
||||
</Text>
|
||||
<Text small light>
|
||||
{metadataValue}
|
||||
</Text>
|
||||
<Text style={styles.bullet}> ● </Text>
|
||||
<Text small light>
|
||||
{dateFormat(new Date(createdAt), BUSINESS_DATE_FORMAT)}
|
||||
</Text>
|
||||
<Text small>{dateFormat(new Date(createdAt), BUSINESS_DATE_FORMAT)}</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
@@ -89,6 +90,9 @@ const styles = StyleSheet.create({
|
||||
bullet: {
|
||||
color: COLORS.TEXT.GREY_1,
|
||||
},
|
||||
lh14: {
|
||||
lineHeight: 14,
|
||||
}
|
||||
});
|
||||
|
||||
export default TrainingMaterialListItem;
|
||||
|
||||
@@ -8,7 +8,7 @@ export const TrainingMaterialContentMap = {
|
||||
[TrainingMaterialContentType.PDF]: {
|
||||
interaction: 'Reading',
|
||||
icon: <TextMaterialIcon />,
|
||||
metadataKey: 'pageCount',
|
||||
metadataKey: 'totalPages',
|
||||
component: PDFFullScreen,
|
||||
},
|
||||
[TrainingMaterialContentType.VIDEO]: {
|
||||
|
||||
@@ -4,9 +4,9 @@ export enum TrainingMaterialContentType {
|
||||
}
|
||||
|
||||
export interface ITrainingMaterial {
|
||||
documentReferenceId: string;
|
||||
topic: string;
|
||||
contentType: TrainingMaterialContentType;
|
||||
referenceId: string;
|
||||
title: string;
|
||||
fileType: TrainingMaterialContentType;
|
||||
createdAt: string;
|
||||
isNewMaterial: boolean;
|
||||
metadata: Record<string, string>;
|
||||
|
||||
Reference in New Issue
Block a user