diff --git a/src/action/TrainingMaterialAction.ts b/src/action/TrainingMaterialAction.ts new file mode 100644 index 00000000..031132be --- /dev/null +++ b/src/action/TrainingMaterialAction.ts @@ -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; + } +}; diff --git a/src/components/pdfRenderer/PdfRenderer.tsx b/src/components/pdfRenderer/PdfRenderer.tsx index e00a743b..9ea815a1 100644 --- a/src/components/pdfRenderer/PdfRenderer.tsx +++ b/src/components/pdfRenderer/PdfRenderer.tsx @@ -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 = ({ docId, url, onPageChange }) => { }, [url]); const handlePageChange = (pageNumber: number) => { - onPageChange?.(pageNumber); + if (isNaN(pageNumber) || !isFunction(onPageChange)) return; + onPageChange(pageNumber + 1); }; return ( diff --git a/src/components/utlis/apiHelper.ts b/src/components/utlis/apiHelper.ts index 03e35bbe..2e7b202f 100644 --- a/src/components/utlis/apiHelper.ts +++ b/src/components/utlis/apiHelper.ts @@ -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 = {} as Record; @@ -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); } diff --git a/src/reducer/trainingMaterialSlice.ts b/src/reducer/trainingMaterialSlice.ts index 119fd42a..a6840ed9 100644 --- a/src/reducer/trainingMaterialSlice.ts +++ b/src/reducer/trainingMaterialSlice.ts @@ -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({ diff --git a/src/screens/trainingMaterial/TrainingMaterial.tsx b/src/screens/trainingMaterial/TrainingMaterial.tsx index 7a509432..fb513538 100644 --- a/src/screens/trainingMaterial/TrainingMaterial.tsx +++ b/src/screens/trainingMaterial/TrainingMaterial.tsx @@ -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 = () => { - {trainingMaterial.map((item) => ( - + {trainingMaterial?.map((item) => ( + ))} diff --git a/src/screens/trainingMaterial/TrainingMaterialDetail.tsx b/src/screens/trainingMaterial/TrainingMaterialDetail.tsx index 8f408fb8..4975b5d0 100644 --- a/src/screens/trainingMaterial/TrainingMaterialDetail.tsx +++ b/src/screens/trainingMaterial/TrainingMaterialDetail.tsx @@ -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 = (props) => { const { @@ -16,38 +19,55 @@ const TrainingMaterialDetail: React.FC = (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({}); + + 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 ( - + - {isVideo ? ( - - ) : ( - - )} + } + loading={fileDetails?.loading} + > + {isVideo ? ( + + ) : ( + + )} + ); diff --git a/src/screens/trainingMaterial/TrainingMaterialListItem.tsx b/src/screens/trainingMaterial/TrainingMaterialListItem.tsx index 321b365f..afa9eeac 100644 --- a/src/screens/trainingMaterial/TrainingMaterialListItem.tsx +++ b/src/screens/trainingMaterial/TrainingMaterialListItem.tsx @@ -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 = ({ 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 = ({ {icon} - - {topic} + + {title} - - {interaction} + {interaction} + + {' '} + ●{' '} + + + {fileType === TrainingMaterialContentType.PDF + ? `${metadataValue} pages` + : metadataValue} {' '} ●{' '} - - {metadataValue} - - - - {dateFormat(new Date(createdAt), BUSINESS_DATE_FORMAT)} - + {dateFormat(new Date(createdAt), BUSINESS_DATE_FORMAT)} @@ -89,6 +90,9 @@ const styles = StyleSheet.create({ bullet: { color: COLORS.TEXT.GREY_1, }, + lh14: { + lineHeight: 14, + } }); export default TrainingMaterialListItem; diff --git a/src/screens/trainingMaterial/constants.tsx b/src/screens/trainingMaterial/constants.tsx index c63c6467..9eed1d22 100644 --- a/src/screens/trainingMaterial/constants.tsx +++ b/src/screens/trainingMaterial/constants.tsx @@ -8,7 +8,7 @@ export const TrainingMaterialContentMap = { [TrainingMaterialContentType.PDF]: { interaction: 'Reading', icon: , - metadataKey: 'pageCount', + metadataKey: 'totalPages', component: PDFFullScreen, }, [TrainingMaterialContentType.VIDEO]: { diff --git a/src/screens/trainingMaterial/interfaces.ts b/src/screens/trainingMaterial/interfaces.ts index 56799fbf..d1d564d3 100644 --- a/src/screens/trainingMaterial/interfaces.ts +++ b/src/screens/trainingMaterial/interfaces.ts @@ -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;