diff --git a/App/Container/Navi-Insurance/Styles/index.ts b/App/Container/Navi-Insurance/Styles/index.ts index adc7a9de36..89e836661e 100644 --- a/App/Container/Navi-Insurance/Styles/index.ts +++ b/App/Container/Navi-Insurance/Styles/index.ts @@ -57,6 +57,12 @@ export const commonStyles = StyleSheet.create({ width16: { width: 16, }, + width12: { + width: 12, + }, + marginRight0: { + marginRight: 0, + }, overflowHidden: { overflow: "hidden", }, diff --git a/App/Container/Navi-Insurance/index.ts b/App/Container/Navi-Insurance/index.ts index c8f15800af..3e01faabfa 100644 --- a/App/Container/Navi-Insurance/index.ts +++ b/App/Container/Navi-Insurance/index.ts @@ -6,3 +6,5 @@ export { default as WaitingPeriodScreen } from "./screen/waiting-period-screen/W export { default as GenericShimmerScreen } from "./screen/generic-shimmer-screen/GenericShimmerScreen"; export { default as GenericErrorScreen } from "./screen/generic-error-screen/GenericErrorScreen"; export { default as BenefitScreen } from "./screen/benefit-screen/BenefitScreen"; +export { default as MigrationBenefitScreen } from "./screen/migration-benefit-screen/MigrationBenefitScreen"; +export { default as RenewalPlanMigrationScreen } from "./screen/renewal-plan-migration-screen/RenewalPlanMigrationScreen"; diff --git a/App/Container/Navi-Insurance/network/MigrationBenefitApi.ts b/App/Container/Navi-Insurance/network/MigrationBenefitApi.ts new file mode 100644 index 0000000000..7e50392157 --- /dev/null +++ b/App/Container/Navi-Insurance/network/MigrationBenefitApi.ts @@ -0,0 +1,48 @@ +import { Dispatch, SetStateAction } from "react"; +import { getXTargetHeaderInfo } from "../../../../network/ApiClient"; +import { post } from "../../../../network/NetworkService"; +import { ActionMetaData } from "../../../common/actions/GenericAction"; +import { + AnalyticsFlowNameConstant, + AnalyticsMethodNameConstant, + ApiMethod, + GI, +} from "../../../common/constants"; +import { CtaData } from "../../../common/interface"; +import { ScreenData } from "../../../common/interface/widgets/screenData/ScreenData"; +import { ScreenState } from "../../../common/screen/BaseScreen"; +import { handleErrorData } from "../../../common/screen/ScreenActionHandler"; + +export const getMigrationBenefitPageData = async ( + screenMetaData: ActionMetaData, + setScreenData: Dispatch>, +) => { + const url = "alchemist/inflate"; + const startTime = Date.now(); + const apiPromise = post>( + url, + screenMetaData.data, + getXTargetHeaderInfo(GI.toLocaleUpperCase()), + ); + apiPromise + .then(response => { + let screenData = response.data.screenStructure as ScreenData; + const updatedScreenData: ScreenData = { + ...screenData, + screenState: ScreenState.LOADED, + }; + setScreenData(updatedScreenData); + }) + .catch(error => { + handleErrorData( + error, + setScreenData, + screenMetaData, + AnalyticsFlowNameConstant.GI_RN_MIGRATION_BENEFIT, + AnalyticsMethodNameConstant.FETCH_MIGRATION_BENEFIT_LIST, + startTime, + url, + ApiMethod.POST, + ); + }); +}; diff --git a/App/Container/Navi-Insurance/network/RenewalPlanMigrationPageApi.ts b/App/Container/Navi-Insurance/network/RenewalPlanMigrationPageApi.ts new file mode 100644 index 0000000000..9f13c9d165 --- /dev/null +++ b/App/Container/Navi-Insurance/network/RenewalPlanMigrationPageApi.ts @@ -0,0 +1,102 @@ +import { Dispatch, SetStateAction } from "react"; +import { getXTargetHeaderInfo } from "../../../../network/ApiClient"; +import { post } from "../../../../network/NetworkService"; +import { ActionMetaData } from "../../../common/actions/GenericAction"; +import { + AnalyticsFlowNameConstant, + AnalyticsMethodNameConstant, + ApiMethod, + CacheKeyConstants, + CacheValueConstants, + GI, +} from "../../../common/constants"; +import { CtaData } from "../../../common/interface"; +import { ScreenData } from "../../../common/interface/widgets/screenData/ScreenData"; +import { ScreenState } from "../../../common/screen/BaseScreen"; +import { handleErrorData as handleScreenErrorData } from "../../../common/screen/ScreenActionHandler"; +import { + getStringPreference, + parseString, + setStringPreference, +} from "../../../common/utilities"; +import { handleResponseData } from "../../../common/widgets/widget-actions/WidgetActionHandler"; +import { RenewalPlanPatchRequest } from "../screen/renewal-plan-migration-screen/types"; + +export const getRenewalPlanMigrationPageData = async ( + screenMetaData: ActionMetaData, + setScreenData: Dispatch>, +) => { + const url = "alchemist/inflate"; + const startTime = Date.now(); + const apiPromise = post>( + url, + screenMetaData.data, + getXTargetHeaderInfo(GI.toLocaleUpperCase()), + ); + apiPromise + .then(response => { + let screenData = response.data.screenStructure as ScreenData; + setScreenData({ + ...screenData, + screenState: ScreenState.LOADED, + }); + }) + .catch(error => { + handleScreenErrorData( + error, + setScreenData, + screenMetaData, + AnalyticsFlowNameConstant.GI_RN_RENEWAL_PLAN_MIGRATION, + AnalyticsMethodNameConstant.RENEWAL_PLAN_MIGRATION_SCREEN, + startTime, + url, + ApiMethod.POST, + ); + }); +}; + +export const handlePatchRenewalQuote = async ({ + widgetMetaData, + ctaData, + setScreenData, + setErrorMetaData, + navigation, + screenData, +}: { + widgetMetaData: ActionMetaData; + setScreenData: Dispatch>; + setErrorMetaData: + | Dispatch> + | undefined; + screenData?: ScreenData | null; + ctaData?: CtaData; + navigation?: any; +}) => { + setScreenData({ + ...screenData, + screenState: ScreenState.OVERLAY, + }); + + const { nextPageCta, bottomSheetCta, showBottomSheet, action } = + widgetMetaData?.data as RenewalPlanPatchRequest; + + const disableBottomSheet = await isBottomSheetDisabled(); + const isBottomSheetVisible = + parseString(showBottomSheet) && + disableBottomSheet !== CacheValueConstants.TRUE; + const nextCta = isBottomSheetVisible ? bottomSheetCta : nextPageCta; + handleResponseData(nextCta, setScreenData, screenData, navigation); + if (disableBottomSheet) return; + const preferenceData = { + shouldPersistForSession: true, + value: CacheValueConstants.TRUE, + }; + setStringPreference( + CacheKeyConstants.DISABLE_BOTTOM_SHEET, + JSON.stringify(preferenceData), + ); +}; + +const isBottomSheetDisabled = async () => { + return await getStringPreference(CacheKeyConstants.DISABLE_BOTTOM_SHEET); +}; diff --git a/App/Container/Navi-Insurance/network/index.ts b/App/Container/Navi-Insurance/network/index.ts index 1a7a222cbf..37070e8f94 100644 --- a/App/Container/Navi-Insurance/network/index.ts +++ b/App/Container/Navi-Insurance/network/index.ts @@ -2,3 +2,5 @@ export * from "./BenefitPageApi"; export * from "./MarketBenefitComparePageApi"; export * from "./QuotePageApi"; export * from "./WaitingPeriodApi"; +export * from "./MigrationBenefitApi"; +export * from "./RenewalPlanMigrationPageApi"; diff --git a/App/Container/Navi-Insurance/screen/benefit-screen/BenefitScreen.tsx b/App/Container/Navi-Insurance/screen/benefit-screen/BenefitScreen.tsx index 516755897a..6fcf98d30f 100644 --- a/App/Container/Navi-Insurance/screen/benefit-screen/BenefitScreen.tsx +++ b/App/Container/Navi-Insurance/screen/benefit-screen/BenefitScreen.tsx @@ -1,16 +1,7 @@ import { useNavigation } from "@react-navigation/native"; import { useEffect } from "react"; -import { - BackHandler, - NativeScrollEvent, - NativeSyntheticEvent, - StatusBar, - View, -} from "react-native"; -import Animated, { - useDerivedValue, - useSharedValue, -} from "react-native-reanimated"; +import { BackHandler, StatusBar, View } from "react-native"; +import Animated from "react-native-reanimated"; import Colors from "../../../../../assets/colors/colors"; import { CommonContentWidgets, @@ -24,7 +15,6 @@ import { BENEFIT_SCREEN, ConstantCta, HARDWARE_BACK_PRESS, - INITIAL_Y_VALUE, } from "../../../../common/constants"; import { sendAsAnalyticsEvent } from "../../../../common/hooks/useAnalyticsEvent"; import { AnalyticsEvent, CtaData } from "../../../../common/interface"; @@ -45,13 +35,7 @@ const BenefitScreen = ({ handleActions, }: BenefitScreenProps) => { const navigation = useNavigation(); - const y = useSharedValue(INITIAL_Y_VALUE); - const onScroll = (event: NativeSyntheticEvent) => { - y.value = event.nativeEvent.contentOffset.y; - }; - const derivedY = useDerivedValue(() => { - return y.value; - }); + const { planId, benefitType, sourceScreen } = extractCtaParameters(ctaData); const handleClick = (cta?: CtaData) => { @@ -216,7 +200,6 @@ const BenefitScreen = ({ nestedScrollEnabled={true} bounces={false} scrollEnabled={true} - onScroll={onScroll} stickyHeaderIndices={[0]} overScrollMode={"never"} > diff --git a/App/Container/Navi-Insurance/screen/benefit-screen/BenefitScreenStyles.ts b/App/Container/Navi-Insurance/screen/benefit-screen/BenefitScreenStyles.ts index 16f047ad95..75030a5a39 100644 --- a/App/Container/Navi-Insurance/screen/benefit-screen/BenefitScreenStyles.ts +++ b/App/Container/Navi-Insurance/screen/benefit-screen/BenefitScreenStyles.ts @@ -28,12 +28,6 @@ const styles = StyleSheet.create({ paddingTop: 16, paddingBottom: 8, }, - headerButton: { - backgroundColor: "#274688", - borderRadius: 16, - paddingVertical: 8, - paddingHorizontal: 16, - }, headerContainerBottomBar: { flexDirection: "column", justifyContent: "flex-end", diff --git a/App/Container/Navi-Insurance/screen/migration-benefit-screen/MigrationBenefitScreen.tsx b/App/Container/Navi-Insurance/screen/migration-benefit-screen/MigrationBenefitScreen.tsx new file mode 100644 index 0000000000..84b813fdde --- /dev/null +++ b/App/Container/Navi-Insurance/screen/migration-benefit-screen/MigrationBenefitScreen.tsx @@ -0,0 +1,248 @@ +import { useNavigation } from "@react-navigation/native"; +import { useEffect } from "react"; +import { BackHandler, StatusBar, View } from "react-native"; +import Animated from "react-native-reanimated"; +import Colors from "../../../../../assets/colors/colors"; +import { + CommonContentWidgets, + CommonFooter, +} from "../../../../../components/reusable"; +import GradientBorder from "../../../../../components/reusable/gradient-border/GradientBorder"; +import { StyledImage, StyledText } from "../../../../../components/widgets"; +import { BaseActionTypes } from "../../../../common/actions/GenericAction"; +import { + AnalyticsEventNameConstants, + AnalyticsFlowNameConstant, + ConstantCta, + HARDWARE_BACK_PRESS, + MIGRATION_BENEFIT_SCREEN, + UrlConstants, +} from "../../../../common/constants"; +import { sendAsAnalyticsEvent } from "../../../../common/hooks/useAnalyticsEvent"; +import { NaviLinearGradient } from "../../../../common/hooks/useGradient"; +import { AnalyticsEvent, CtaData } from "../../../../common/interface"; +import { + MigrationBenefitScreenHeaderData, + MigrationBenefitScreenProps, +} from "../../../../common/interface/widgets/widgetData/MigrationBenefitScreenHeaderData"; +import { ScreenState } from "../../../../common/screen/BaseScreen"; +import { ScreenActionTypes } from "../../../../common/screen/ScreenActionTypes"; +import { extractCtaParameters } from "../../../../common/utilities/CtaParamsUtils"; +import { globalHandleClick } from "../../../../common/utilities/NavigationUtil"; +import { isValidHexColor } from "../../../../common/utilities/ValidateColors"; +import QuoteOfferErrorScreen from "../quote-offer-screen/error-screen/QuoteOfferErrorScreen"; +import WaitingPeriodShimmerScreen from "../waiting-period-screen/shimmer-screen/WaitingPeriodShimmerScreen"; +import styles from "./MigrationBenefitScreenStyles"; + +const MigrationBenefitScreen = ({ + ctaData, + screenData, + handleActions, +}: MigrationBenefitScreenProps) => { + const navigation = useNavigation(); + + const { planId, previousPlanId, preQuoteId, planType } = + extractCtaParameters(ctaData); + + const handleClick = (cta?: CtaData) => { + return globalHandleClick(navigation, cta, MIGRATION_BENEFIT_SCREEN); + }; + + useEffect(() => { + const initEvent: AnalyticsEvent = { + name: AnalyticsEventNameConstants.HI_RN_MIGRATION_BENEFIT_INIT, + properties: { + screenName: AnalyticsFlowNameConstant.GI_RN_MIGRATION_BENEFIT, + planId: planId || "", + previousPlanId: previousPlanId || "", + preQuoteId: preQuoteId || "", + planType: planType || "", + }, + }; + sendAsAnalyticsEvent(initEvent); + + const data: MigrationBenefitScreenRequestData = { + inputMap: { + planId: planId, + previousPlanId: previousPlanId, + preQuoteId: preQuoteId, + planType: planType, + } as MigrationBenefitScreenRequest, + screenName: UrlConstants.MIGRATION_BENEFIT_SCREEN_URL, + }; + + handleActions({ + baseActionType: BaseActionTypes.SCREEN_ACTION, + metaData: [ + { + actionType: ScreenActionTypes.FETCH_MIGRATION_BENEFIT_LIST, + data, + screenName: MIGRATION_BENEFIT_SCREEN, + }, + ], + }); + }, [ctaData]); + + let headerData = screenData?.screenWidgets?.headerWidgets?.at( + 0, + ) as MigrationBenefitScreenHeaderData; + + let headerColor = isValidHexColor(headerData?.backgroundColor || "") + ? headerData?.backgroundColor + : Colors.white; + let backCta = headerData?.leftIcon?.cta; + const handleBackButtonClick = () => { + handleClick && backCta && handleClick(backCta); + return true; + }; + + useEffect(() => { + if (backCta) { + BackHandler.addEventListener(HARDWARE_BACK_PRESS, handleBackButtonClick); + } + return () => { + if (backCta) { + BackHandler.removeEventListener( + HARDWARE_BACK_PRESS, + handleBackButtonClick, + ); + } + }; + }, [screenData]); + + const Header = () => { + let headerData = screenData?.screenWidgets?.headerWidgets?.at( + 0, + ) as MigrationBenefitScreenHeaderData; + return ( + + + {headerData?.leftIcon && ( + + )} + {headerData?.title && ( + + )} + {headerData?.rightIcon ? ( + + ) : ( + + )} + + + ); + }; + + const TopSection = () => { + return ( + + {headerData?.description?.url && ( + + + + + + {headerData?.tagLine && ( + + )} + + + + + )} + + ); + }; + + useEffect(() => { + switch (screenData?.screenState) { + case ScreenState.LOADED: + const loadedEvent: AnalyticsEvent = { + name: AnalyticsEventNameConstants.HI_RN_MIGRATION_BENEFIT_LOADED_VIEW, + properties: { + screenName: AnalyticsFlowNameConstant.GI_RN_MIGRATION_BENEFIT, + planId: planId || "", + previousPlanId: previousPlanId || "", + preQuoteId: preQuoteId || "", + planType: planType || "", + }, + }; + sendAsAnalyticsEvent(loadedEvent); + break; + case ScreenState.ERROR: + const errorEvent: AnalyticsEvent = { + name: AnalyticsEventNameConstants.HI_RN_MIGRATION_BENEFIT_ERROR_VIEW, + properties: { + screenName: AnalyticsFlowNameConstant.GI_RN_MIGRATION_BENEFIT, + planId: planId || "", + previousPlanId: previousPlanId || "", + preQuoteId: preQuoteId || "", + planType: planType || "", + }, + }; + sendAsAnalyticsEvent(errorEvent); + break; + default: + break; + } + }, [screenData?.screenState]); + + if (screenData?.screenState === ScreenState.LOADING) { + return ; + } + + if (screenData?.screenState === ScreenState.ERROR) { + return ( + + ); + } + + const getPointerEvents = () => + screenData?.screenState === ScreenState.OVERLAY ? "none" : "auto"; + + return ( + + +
+ + + + + + + ); +}; + +export default MigrationBenefitScreen; diff --git a/App/Container/Navi-Insurance/screen/migration-benefit-screen/MigrationBenefitScreenStyles.ts b/App/Container/Navi-Insurance/screen/migration-benefit-screen/MigrationBenefitScreenStyles.ts new file mode 100644 index 0000000000..4587c1328e --- /dev/null +++ b/App/Container/Navi-Insurance/screen/migration-benefit-screen/MigrationBenefitScreenStyles.ts @@ -0,0 +1,52 @@ +import { StyleSheet } from "react-native"; + +const styles = StyleSheet.create({ + container: { + flex: 1, + flexDirection: "column", + backgroundColor: "#FFFFFF", + }, + content: { + flexGrow: 1, + backgroundColor: "#FFFFFF", + }, + footer: { + alignItems: "stretch", + }, + headerContainer: { + flexDirection: "column", + justifyContent: "center", + alignItems: "center", + }, + headerContainerTopBar: { + zIndex: 1, + flexDirection: "row", + justifyContent: "space-between", + alignItems: "center", + width: "100%", + padding: 16, + }, + headerContainerBottomBar: { + flexDirection: "column", + justifyContent: "flex-end", + alignItems: "center", + width: "100%", + }, + spacer: { + height: 8, + }, + placeholderImage: { + height: 24, + width: 24, + }, + tagStyle: { + paddingHorizontal: 12, + paddingVertical: 4, + }, + tagLineContainer: { + position: "absolute", + paddingBottom: 12, + }, +}); + +export default styles; diff --git a/App/Container/Navi-Insurance/screen/renewal-plan-migration-screen/RenewalPlanMigrationScreen.tsx b/App/Container/Navi-Insurance/screen/renewal-plan-migration-screen/RenewalPlanMigrationScreen.tsx new file mode 100644 index 0000000000..80a521277e --- /dev/null +++ b/App/Container/Navi-Insurance/screen/renewal-plan-migration-screen/RenewalPlanMigrationScreen.tsx @@ -0,0 +1,158 @@ +import { useNavigation } from "@react-navigation/native"; +import React, { useEffect } from "react"; +import { NativeScrollEvent, NativeSyntheticEvent, View } from "react-native"; +import Animated, { + useDerivedValue, + useSharedValue, +} from "react-native-reanimated"; +import Colors from "../../../../../assets/colors/colors"; +import { + CommonContentWidgets, + CommonHeader, +} from "../../../../../components/reusable"; +import WidgetViewRenderer from "../../../../../components/widget-view-renderer/WidgetViewRenderer"; +import { BaseActionTypes } from "../../../../common/actions/GenericAction"; +import { + CacheKeyConstants, + CacheValueConstants, + ConstantCta, + INITIAL_Y_VALUE, + RENEWAL_PLAN_MIGRATION_SCREEN, + UrlConstants, +} from "../../../../common/constants"; +import { useAnalyticsEvent } from "../../../../common/hooks"; +import { NaviLinearGradient } from "../../../../common/hooks/useGradient"; +import { CtaData } from "../../../../common/interface"; +import { BaseScreenProps } from "../../../../common/interface/BaseScreenProps"; +import { ScreenState } from "../../../../common/screen/BaseScreen"; +import { ScreenActionTypes } from "../../../../common/screen/ScreenActionTypes"; +import { setStringPreference } from "../../../../common/utilities"; +import { extractCtaParameters } from "../../../../common/utilities/CtaParamsUtils"; +import { globalHandleClick } from "../../../../common/utilities/NavigationUtil"; +import { commonHeaderShadowStyle } from "../../Styles"; +import QuoteOfferErrorScreen from "../quote-offer-screen/error-screen/QuoteOfferErrorScreen"; +import styles from "./RenewalPlanMigrationScreenStyles"; +import RenewalPlanMigrationShimmerScreen from "./shimmer-screen/RenewalPlanMigrationShimmerScreen"; +import { screenEvents } from "./types"; + +const RenewalPlanMigrationScreen = ({ + ctaData, + screenData, + handleActions, +}: BaseScreenProps) => { + const { sendScreenStateAnalyticsEvents } = useAnalyticsEvent(); + const navigation = useNavigation(); + const y = useSharedValue(INITIAL_Y_VALUE); + const derivedY = useDerivedValue(() => { + return y.value; + }); + const onScroll = (event: NativeSyntheticEvent) => { + y.value = event.nativeEvent.contentOffset.y; + }; + + const { preQuoteId } = extractCtaParameters(ctaData); + + const data: RenewalPlanMigrationScreenRequestData = { + inputMap: { + preQuoteId: preQuoteId, + } as RenewalPlanMigrationScreenRequest, + screenName: UrlConstants.RENEWAL_PLAN_MIGRATION_SCREEN_URL, + }; + + useEffect(() => { + handleActions({ + baseActionType: BaseActionTypes.SCREEN_ACTION, + metaData: [ + { + actionType: ScreenActionTypes.FETCH_RENEWAL_PLAN_MIGRATION_SCREEN, + data, + screenName: RENEWAL_PLAN_MIGRATION_SCREEN, + }, + ], + }); + }, [ctaData]); + + const handleClick = (cta?: CtaData) => { + if (!cta) return; + + const { toggleNextCta } = extractCtaParameters(cta); + + if (toggleNextCta) { + const preferenceData = { + shouldPersistForSession: true, + value: CacheValueConstants.TRUE, + }; + setStringPreference( + CacheKeyConstants.DISABLE_BOTTOM_SHEET, + JSON.stringify(preferenceData), + ); + } + globalHandleClick(navigation, cta, RENEWAL_PLAN_MIGRATION_SCREEN); + }; + + const headerStyle = commonHeaderShadowStyle(derivedY); + + const getPointerEvents = () => + screenData?.screenState === ScreenState.OVERLAY ? "none" : "auto"; + + sendScreenStateAnalyticsEvents(screenEvents, screenData?.screenState); + + if (screenData?.screenState === ScreenState.LOADING) { + return ; + } + + if (screenData?.screenState === ScreenState.ERROR) { + return ( + + ); + } + return ( + + + + + + + + + + + ); +}; + +export default RenewalPlanMigrationScreen; diff --git a/App/Container/Navi-Insurance/screen/renewal-plan-migration-screen/RenewalPlanMigrationScreenStyles.ts b/App/Container/Navi-Insurance/screen/renewal-plan-migration-screen/RenewalPlanMigrationScreenStyles.ts new file mode 100644 index 0000000000..798b52a9cd --- /dev/null +++ b/App/Container/Navi-Insurance/screen/renewal-plan-migration-screen/RenewalPlanMigrationScreenStyles.ts @@ -0,0 +1,28 @@ +import { StyleSheet } from "react-native"; + +const styles = StyleSheet.create({ + container: { + flex: 1, + flexDirection: "column", + }, + header: { + width: "100%", + alignItems: "stretch", + backgroundColor: "#FFFFFF", + zIndex: 1, + }, + content: { + flexGrow: 1, + }, + footerContainer: { + position: "absolute", + bottom: 0, + width: "100%", + }, + footerGradient: { + height: 35, + marginBottom: -8, + }, +}); + +export default styles; diff --git a/App/Container/Navi-Insurance/screen/renewal-plan-migration-screen/shimmer-screen/RenewalPlanMigrationShimmerScreen.tsx b/App/Container/Navi-Insurance/screen/renewal-plan-migration-screen/shimmer-screen/RenewalPlanMigrationShimmerScreen.tsx new file mode 100644 index 0000000000..24449c5b06 --- /dev/null +++ b/App/Container/Navi-Insurance/screen/renewal-plan-migration-screen/shimmer-screen/RenewalPlanMigrationShimmerScreen.tsx @@ -0,0 +1,43 @@ +import { View } from "react-native"; +import SkeletonPlaceholder from "react-native-skeleton-placeholder"; +import Colors from "../../../../../../assets/colors/colors"; +import { CtaData } from "../../../../../common/interface"; +import styles from "./RenewalPlanMigrationShimmerScreenStyles"; + +const RenewalPlanMigrationShimmerScreen = ({ + handleClick, +}: { + handleClick?: (ctaData: CtaData) => void; +}) => { + return ( + + + + ); +}; + +export default RenewalPlanMigrationShimmerScreen; + +const ContentShimmer = () => { + return ( + + + + + + + + + + + + + + + ); +}; diff --git a/App/Container/Navi-Insurance/screen/renewal-plan-migration-screen/shimmer-screen/RenewalPlanMigrationShimmerScreenStyles.ts b/App/Container/Navi-Insurance/screen/renewal-plan-migration-screen/shimmer-screen/RenewalPlanMigrationShimmerScreenStyles.ts new file mode 100644 index 0000000000..00b672ea1e --- /dev/null +++ b/App/Container/Navi-Insurance/screen/renewal-plan-migration-screen/shimmer-screen/RenewalPlanMigrationShimmerScreenStyles.ts @@ -0,0 +1,45 @@ +import { StyleSheet } from "react-native"; + +const styles = StyleSheet.create({ + container: { + flex: 1, + flexDirection: "column", + justifyContent: "center", + }, + content: { + marginTop: 20, + flexGrow: 1, + marginHorizontal: 16, + }, + headerShimmerLayout: { + flexDirection: "row", + justifyContent: "space-between", + marginBottom: 24, + }, + header1: { + borderRadius: 16, + height: 24, + width: 24, + }, + header2: { + borderRadius: 16, + height: 24, + width: 40, + }, + shimmerLayout1: { + borderRadius: 16, + alignItems: "center", + height: 24, + width: "75%", + }, + shimmerLayout2: { + borderRadius: 16, + marginTop: 24, + height: 152, + alignItems: "center", + flexShrink: 0, + gap: 10.5, + }, +}); + +export default styles; diff --git a/App/Container/Navi-Insurance/screen/renewal-plan-migration-screen/types.ts b/App/Container/Navi-Insurance/screen/renewal-plan-migration-screen/types.ts new file mode 100644 index 0000000000..26b24a08de --- /dev/null +++ b/App/Container/Navi-Insurance/screen/renewal-plan-migration-screen/types.ts @@ -0,0 +1,31 @@ +import { GenericActionPayload } from "../../../../common/actions/GenericAction"; +import { + AnalyticsEventNameConstants, + AnalyticsFlowNameConstant, +} from "../../../../common/constants"; +import { AnalyticsEvent, CtaData } from "../../../../common/interface"; +import { ScreenState } from "../../../../common/screen/BaseScreen"; +import { SumInsuredRequestData } from "../../network"; + +export const screenEvents: Partial> = { + [ScreenState.LOADED]: { + name: AnalyticsEventNameConstants.HI_RN_RENEWAL_PLAN_MIGRATION_PAGE_INIT, + properties: { + screen: AnalyticsFlowNameConstant.GI_RN_RENEWAL_PLAN_MIGRATION, + }, + }, + [ScreenState.ERROR]: { + name: AnalyticsEventNameConstants.HI_RN_RENEWAL_PLAN_MIGRATION_PAGE_ERROR_VIEW, + properties: { + screen: AnalyticsFlowNameConstant.GI_RN_RENEWAL_PLAN_MIGRATION, + }, + }, +}; + +export interface RenewalPlanPatchRequest { + requestData: SumInsuredRequestData; + showBottomSheet: string; + nextPageCta: CtaData; + bottomSheetCta: CtaData; + action?: GenericActionPayload; +} diff --git a/App/common/constants/AnalyticsEventsConstant.ts b/App/common/constants/AnalyticsEventsConstant.ts index 0449c72765..490e3cc952 100644 --- a/App/common/constants/AnalyticsEventsConstant.ts +++ b/App/common/constants/AnalyticsEventsConstant.ts @@ -9,10 +9,17 @@ export const AnalyticsEventNameConstants = { HI_RN_COMPARE_PLAN_PAGE_ERROR_VIEW: "hi_rn_compare_plan_page_error_view", HI_RN_WAITING_PERIOD_PAGE_INIT: "hi_rn_waiting_period_page_init", HI_RN_WAITING_PERIOD_PAGE_ERROR_VIEW: "hi_rn_waiting_period_page_error_view", + HI_RN_RENEWAL_PLAN_MIGRATION_PAGE_INIT: + "hi_rn_renewal_plan_migration_page_init", + HI_RN_RENEWAL_PLAN_MIGRATION_PAGE_ERROR_VIEW: + "hi_rn_renewal_plan_migration_page_error_view", PATCH_QUOTE_V2: "patch_quote_v2", HI_RN_BENEFIT_INIT: "hi_rn_benefit_init", HI_RN_BENEFIT_ERROR_VIEW: "hi_rn_benefit_error_view", HI_RN_BENEFIT_LOADED_VIEW: "hi_rn_benefit_loaded_view", + HI_RN_MIGRATION_BENEFIT_INIT: "hi_rn_migration_benefit_init", + HI_RN_MIGRATION_BENEFIT_ERROR_VIEW: "hi_rn_migration_benefit_error_view", + HI_RN_MIGRATION_BENEFIT_LOADED_VIEW: "hi_rn_migration_benefit_loaded_view", HI_RN_BACKGROUND_API_FAILED: "hi_rn_background_api_failed", HI_RN_CACHE_ERROR: "hi_rn_cache_error", HI_RN_CACHE_FETCH_SUCCESS: "hi_rn_cache_fetch_success", @@ -44,7 +51,9 @@ export const AnalyticsFlowNameConstant = { GI_WAITING_PERIOD: "hi_rn_waiting_period", REACT_NATIVE: "react_native", GI_RN_BENEFIT: "hi_rn_benefit", + GI_RN_MIGRATION_BENEFIT: "hi_rn_migration_benefit", GI_RN_COMPARE_PLAN: "hi_rn_compare_plan", + GI_RN_RENEWAL_PLAN_MIGRATION: "hi_rn_renewal_plan_migration", }; export const AnalyticsModuleNameConstant = { @@ -56,10 +65,13 @@ export const AnalyticsMethodNameConstant = { FETCH_QUOTE_V3: "fetchQuoteV3", FETCH_QUOTE_V4: "fetchQuoteV4", FINAL_PATCH_CALL: "finalPatchCall", + PATCH_RENEWAL_QUOTE: "gi_update_renewal_quote_error", COMPARE_PLAN_LIST: "comparePlanList", MARKET_BENEFIT_COMPARE_LIST: "gi_market_benefit_compare_list_error", FETCH_BENEFIT_LIST: "gi_fetch_benefit_screen_error", + FETCH_MIGRATION_BENEFIT_LIST: "gi_fetch_migration_benefit_screen_error", WAITING_PERIOD_SCREEN: "gi_waiting_period_screen_error", + RENEWAL_PLAN_MIGRATION_SCREEN: "gi_renewal_plan_migration_screen_error", HANDLE_CTA_CLICK: "handleCtaClick", HANDLE_CTA_CLICK_BOTTOMSHEET: "handleCtaClickBottomSheet", }; diff --git a/App/common/constants/NumericalConstants.ts b/App/common/constants/NumericalConstants.ts index 622f06ca19..1ca8134a61 100644 --- a/App/common/constants/NumericalConstants.ts +++ b/App/common/constants/NumericalConstants.ts @@ -4,6 +4,8 @@ export const BOTTOMSHEET_ANIMATION_DURATION = 200; export const INITIAL_Y_VALUE = 0; export const THRESHOLD_Y_BENEFIT = 50; export const SCREEN_MID = 0.5; +export const BENEFIT_SECTION_ANIMATION_DURATION = 400; +export const THROTTLE_DURATION = 700; export enum CommonHeaderShadowStyleProperties { INITIAL_SHADOW_OPACITY = 0, diff --git a/App/common/constants/ScreenNameConstants.ts b/App/common/constants/ScreenNameConstants.ts index d3841e4e43..236589b9f8 100644 --- a/App/common/constants/ScreenNameConstants.ts +++ b/App/common/constants/ScreenNameConstants.ts @@ -11,4 +11,6 @@ export const MARKET_BENEFITS_COMPARE_SCREEN = "market_benefits_compare"; export const COMPARE_PLAN_SCREEN = "compare_plans"; export const WAITING_PERIOD_SCREEN = "waiting_period"; export const BENEFIT_SCREEN = "benefit"; +export const MIGRATION_BENEFIT_SCREEN = "migration_benefit"; +export const RENEWAL_PLAN_MIGRATION_SCREEN = "renewal_plan_migration"; export const GENERIC_ERROR_SCREEN = "generic_error"; diff --git a/App/common/constants/StringConstant.ts b/App/common/constants/StringConstant.ts index 6699c0c05f..1a0c3142f0 100644 --- a/App/common/constants/StringConstant.ts +++ b/App/common/constants/StringConstant.ts @@ -47,6 +47,7 @@ export const QUOTE_APOLOGY_SUBTITLE = export const QUOTE_APOLOGY_BUTTON = "Buy new policy"; export const QUOTE_PATCH_FAIL_TOAST = "Failed. Try again"; export const QUOTE_ID = "quoteId"; +export const PRE_QUOTE_ID = "preQuoteId"; export const APPLICATION_ID = "applicationId"; export const BUILD_CONFIG_DETAILS = "BUILD_CONFIG_DETAILS"; export const SPACE_UNICODE = "\u00A0"; @@ -66,3 +67,17 @@ export enum SeparatorOrientationType { VERTICAL = "vertical", HORIZONTAL = "horizontal", } + +export const UrlConstants = { + MIGRATION_BENEFIT_SCREEN_URL: "GI_MIGRATION_BENEFIT", + RENEWAL_PLAN_MIGRATION_SCREEN_URL: "GI_RENEWAL_PLAN_MIGRATION_SCREEN", +}; + +export enum CacheKeyConstants { + DISABLE_BOTTOM_SHEET = "DisableBottomSheet", +} + +export enum CacheValueConstants { + TRUE = "true", + FALSE = "false", +} diff --git a/App/common/constants/WidgetNameConstants.ts b/App/common/constants/WidgetNameConstants.ts index 5e43f584e0..e25c9ef7bf 100644 --- a/App/common/constants/WidgetNameConstants.ts +++ b/App/common/constants/WidgetNameConstants.ts @@ -24,13 +24,13 @@ export const HERO_SECTION_WIDGET = "HERO_SECTION_WIDGET"; export const SELECT_CARD_WITH_DETAIL_LIST_WIDGET = "SELECT_CARD_WITH_DETAIL_LIST_WIDGET"; export const LIST_ITEM_WITH_ICON_WIDGET = "LIST_ITEM_WITH_ICON_WIDGET"; - export const CARD_WITH_LIST_ITEMS_WIDGET = "CARD_WITH_LIST_ITEMS_WIDGET"; export const TITLE_WITH_HORIZONTAL_CAROUSEL_LIST_WIDGET = "TITLE_WITH_HORIZONTAL_CAROUSEL_LIST_WIDGET"; - export const TITLE_RIGHT_TITLE_WITH_CONTENT_LIST_WIDGET = "TITLE_RIGHT_TITLE_WITH_CONTENT_LIST_WIDGET"; +export const EXPANDABLE_LIST_WIDGET = "EXPANDABLE_LIST_WIDGET"; +export const SELECT_CARD_WITH_FOOTER_WIDGET = "SELECT_CARD_WITH_FOOTER_WIDGET"; export const SELECT_CARD_WITH_TAG_LIST_ITEMS_WIDGET = "SELECT_CARD_WITH_TAG_LIST_ITEMS_WIDGET"; diff --git a/App/common/interface/components/WidgetComponentData.ts b/App/common/interface/components/WidgetComponentData.ts index d720e61da5..98a17be46b 100644 --- a/App/common/interface/components/WidgetComponentData.ts +++ b/App/common/interface/components/WidgetComponentData.ts @@ -14,6 +14,7 @@ export interface WidgetComponentProps { ) => void; handleClick?: (cta: CtaData) => void; style?: StyleProp>>; + pointerEvents?: "auto" | "none" | "box-none" | "box-only"; } export interface WidgetViewRendererProps { @@ -24,4 +25,5 @@ export interface WidgetViewRendererProps { ) => void; screenState?: ScreenState | null; handleClick?: (ctaData: CtaData) => void; + pointerEvents?: "auto" | "none" | "box-none" | "box-only"; } diff --git a/App/common/interface/components/index.ts b/App/common/interface/components/index.ts index 79627dc38e..2cf6b822e9 100644 --- a/App/common/interface/components/index.ts +++ b/App/common/interface/components/index.ts @@ -2,3 +2,5 @@ export type { WidgetComponentProps, WidgetViewRendererProps, } from "./WidgetComponentData"; + +export type { GradientBorderData } from "./GradientBorderData"; diff --git a/App/common/interface/index.ts b/App/common/interface/index.ts index 3908b536a1..774da3bfe1 100644 --- a/App/common/interface/index.ts +++ b/App/common/interface/index.ts @@ -1,4 +1,5 @@ import { ImageName } from "../constants"; +import { ScreenData } from "./widgets/screenData/ScreenData"; export type CtaData = { url: string; @@ -10,6 +11,7 @@ export type CtaData = { screenKey?: string; title?: string; analyticsEventProperties?: AnalyticsEvent; + screenStructure?: ScreenData; }; export type AnalyticsEvent = { @@ -56,6 +58,8 @@ export enum ParameterType { SOURCE_SCREEN = "sourceScreen", APPLICATION_ID = "applicationId", SOURCE = "source", + PREVIOUS_PLAN_ID = "previousPlanId", + PLAN_TYPE = "planType", } export enum CtaType { diff --git a/App/common/interface/widgets/modalData/PremiumDetailsBottomSheetData.ts b/App/common/interface/widgets/modalData/PremiumDetailsBottomSheetData.ts index 46cc0a1fcb..0862769bc6 100644 --- a/App/common/interface/widgets/modalData/PremiumDetailsBottomSheetData.ts +++ b/App/common/interface/widgets/modalData/PremiumDetailsBottomSheetData.ts @@ -16,8 +16,9 @@ export interface PremiumDetailsBottomSheetData extends GenericWidgetData { } export interface KeyValueInfoData extends GenericWidgetData { + isVisible?: boolean; key?: TextFieldData; value?: TextFieldData; displayRightOfKey?: TextFieldData; displayLeftOfValue?: TextFieldData; -} \ No newline at end of file +} diff --git a/App/common/interface/widgets/modalData/TitleSubtitleWithDropdownBottomSheetData.ts b/App/common/interface/widgets/modalData/TitleSubtitleWithDropdownBottomSheetData.ts index 0dfa86c65e..7af95f0c18 100644 --- a/App/common/interface/widgets/modalData/TitleSubtitleWithDropdownBottomSheetData.ts +++ b/App/common/interface/widgets/modalData/TitleSubtitleWithDropdownBottomSheetData.ts @@ -17,6 +17,7 @@ export interface TitleSubtitleWithDropdownBottomSheetData headerDescriptionIcon?: ImageFieldData; backgroundGradient?: string[]; gradientOrientation?: string; + showDescriptionIcon?: boolean; } export interface TitleSubtitleWithDropdownBottomSheetProps { diff --git a/App/common/interface/widgets/widgetData/ExpandableListWidgetData.ts b/App/common/interface/widgets/widgetData/ExpandableListWidgetData.ts new file mode 100644 index 0000000000..40a258835f --- /dev/null +++ b/App/common/interface/widgets/widgetData/ExpandableListWidgetData.ts @@ -0,0 +1,26 @@ +import { ViewStyle } from "react-native"; +import { AnalyticsEvent, CtaData } from "../.."; +import { GenericActionPayload } from "../../../actions/GenericAction"; +import { GenericWidgetData } from "../Widget"; +import { TitleWithAssetsWidgetData } from "./TitleWithAssetsWidgetData"; +import { TextFieldData } from "./TitleWidgetData"; + +export interface ExpandableListWidgetProps { + widgetData: ExpandableListWidgetData; + widgetStyle?: ViewStyle; + handleActions: ( + value: any | undefined | null, + actionPayloadList: GenericActionPayload | undefined, + ) => void; + handleClick?: (cta: CtaData) => void; + widgetIndex?: number; +} + +export interface ExpandableListWidgetData extends GenericWidgetData { + sectionTitle?: TextFieldData; + listStyle?: ViewStyle; + listItems?: TitleWithAssetsWidgetData[]; + expandText?: TextFieldData; + collapseText?: TextFieldData; + toggleEvent?: AnalyticsEvent; +} diff --git a/App/common/interface/widgets/widgetData/MigrationBenefitScreenHeaderData.ts b/App/common/interface/widgets/widgetData/MigrationBenefitScreenHeaderData.ts new file mode 100644 index 0000000000..502278e316 --- /dev/null +++ b/App/common/interface/widgets/widgetData/MigrationBenefitScreenHeaderData.ts @@ -0,0 +1,24 @@ +import { CtaData } from "../.."; +import { GenericActionPayload } from "../../../actions/GenericAction"; +import { GradientBorderData } from "../../components/GradientBorderData"; +import { GenericWidgetData } from "../Widget"; +import { ScreenData } from "../screenData/ScreenData"; +import { ImageFieldData, TextFieldData } from "./TitleWidgetData"; + +export interface MigrationBenefitScreenHeaderData extends GenericWidgetData { + title?: TextFieldData; + borderData?: GradientBorderData; + leftIcon?: ImageFieldData; + rightIcon?: ImageFieldData; + description?: ImageFieldData; + backgroundColor?: string; + tagLine?: TextFieldData; + tagLineGradient?: string[]; + tagLineGradientOrientation?: string; +} + +export interface MigrationBenefitScreenProps { + ctaData: CtaData; + screenData: ScreenData | null; + handleActions: (screenPayload?: GenericActionPayload) => void; +} diff --git a/App/common/interface/widgets/widgetData/SelectCardWithFooterWidgetData.ts b/App/common/interface/widgets/widgetData/SelectCardWithFooterWidgetData.ts new file mode 100644 index 0000000000..a33ce15406 --- /dev/null +++ b/App/common/interface/widgets/widgetData/SelectCardWithFooterWidgetData.ts @@ -0,0 +1,81 @@ +import { Dispatch, SetStateAction } from "react"; +import { ViewStyle } from "react-native"; +import { AnalyticsEvent, CtaData } from "../.."; +import { GenericActionPayload } from "../../../actions/GenericAction"; +import { GradientBorderData } from "../../components"; +import { GenericWidgetData } from "../Widget"; +import { ItemAnalyticsEvents } from "./SelectCardWithDetailListData"; +import { ImageFieldData, TextFieldData } from "./TitleWidgetData"; + +export interface SelectionCardItemData { + cardImage?: ImageFieldData; + title?: TextFieldData; + tagCallout?: CalloutData; + footerLeftTitle?: TextFieldData; + footerRightTitle?: TextFieldData; + itemType?: string; + dependentWidgets?: any; + analyticEvents?: ItemAnalyticsEvents; +} + +interface CalloutData { + borderStyle?: GradientBorderData; + title?: TextFieldData; + gradientColors?: string[]; + gradientOrientation?: string; + tagStyle?: ViewStyle; +} + +interface WidgetMetaData { + selectedItem?: string; + onValueChangeAction?: GenericActionPayload; +} + +export interface SelectCardWithFooterWidgetData extends GenericWidgetData { + title?: TextFieldData; + items?: SelectionCardItemData[]; + visibleItems?: number; + accordionData?: AccordionData; + widgetMetaData?: WidgetMetaData; +} + +export interface SelectCardWithFooterWidgetProps { + widgetData: SelectCardWithFooterWidgetData; + widgetStyle: ViewStyle; + handleActions: ( + value?: any | undefined | null, + screenActionPayload?: GenericActionPayload, + ) => void; + handleClick?: (cta: CtaData) => void; + widgetIndex: number; +} + +export interface SelectionCardItemProps { + handleActions: ( + value?: any | undefined | null, + screenActionPayload?: GenericActionPayload, + ) => void; + handleClick?: (cta: CtaData) => void; + item?: SelectionCardItemData; + selected?: boolean; + onSelect?: () => void; +} +export interface AccordionData { + accordionStyle?: ViewStyle; + gradientColors?: string[]; + title?: TextFieldData; + subtitle?: TextFieldData; + rightIcon?: ImageFieldData; + analyticsEventProperties?: AnalyticsEvent; +} + +export interface AccordionProps { + widgetData: SelectCardWithFooterWidgetData; + setNumItems: Dispatch>; + setIsAccordionVisible: Dispatch>; + handleActions: ( + value?: any | undefined | null, + screenActionPayload?: GenericActionPayload, + ) => void; + handleClick?: (cta: CtaData) => void; +} diff --git a/App/common/interface/widgets/widgetData/index.ts b/App/common/interface/widgets/widgetData/index.ts index 0523cc718b..8f9b21e736 100644 --- a/App/common/interface/widgets/widgetData/index.ts +++ b/App/common/interface/widgets/widgetData/index.ts @@ -71,3 +71,12 @@ export type { TitleWithListWidgetData, TitleWithListWidgetDataProps, } from "./TitleWithListWidgetData"; + +export type { + SelectionCardItemData, + SelectionCardItemProps, + SelectCardWithFooterWidgetData, + SelectCardWithFooterWidgetProps, + AccordionData, + AccordionProps, +} from "./SelectCardWithFooterWidgetData"; diff --git a/App/common/screen/ScreenActionHandler.tsx b/App/common/screen/ScreenActionHandler.tsx index 28b030dec4..1c0519a047 100644 --- a/App/common/screen/ScreenActionHandler.tsx +++ b/App/common/screen/ScreenActionHandler.tsx @@ -4,7 +4,9 @@ import { fetchComparisonPlanList, getBenefitPageData, getMarketBenefitComparePageData, + getMigrationBenefitPageData, getQuotePageData, + getRenewalPlanMigrationPageData, getWaitingPeriodScreenData, } from "../../Container/Navi-Insurance/network"; import { ActionMetaData, BaseActionTypes } from "../actions/GenericAction"; @@ -51,6 +53,14 @@ export const ScreenActionHandler = { case ScreenActionTypes.FETCH_BENEFIT_LIST: { return getBenefitPageData(screenMetaData, setScreenData); } + case ScreenActionTypes.FETCH_MIGRATION_BENEFIT_LIST: { + return getMigrationBenefitPageData(screenMetaData, setScreenData); + } + + case ScreenActionTypes.FETCH_RENEWAL_PLAN_MIGRATION_SCREEN: { + return getRenewalPlanMigrationPageData(screenMetaData, setScreenData); + } + case ScreenActionTypes.SHOW_LOADER: { const updatedScreenData: ScreenData = { ...screenData, diff --git a/App/common/screen/ScreenActionTypes.ts b/App/common/screen/ScreenActionTypes.ts index d98f97b0fb..a99750fd23 100644 --- a/App/common/screen/ScreenActionTypes.ts +++ b/App/common/screen/ScreenActionTypes.ts @@ -7,5 +7,7 @@ export const ScreenActionTypes = { FETCH_BENEFIT_COMPARE_LIST: "FETCH_BENEFIT_COMPARE_LIST", FETCH_COMPARE_PLAN_LIST: "FETCH_COMPARE_PLAN_LIST", FETCH_BENEFIT_LIST: "FETCH_BENEFIT_LIST", + FETCH_MIGRATION_BENEFIT_LIST: "FETCH_MIGRATION_BENEFIT_LIST", FETCH_WAITING_PERIOD_SCREEN: "FETCH_WAITING_PERIOD_SCREEN", + FETCH_RENEWAL_PLAN_MIGRATION_SCREEN: "FETCH_RENEWAL_PLAN_MIGRATION_SCREEN", }; diff --git a/App/common/screen/screen-mappers/GIScreenMapper.tsx b/App/common/screen/screen-mappers/GIScreenMapper.tsx index 855e41051a..d50b529e55 100644 --- a/App/common/screen/screen-mappers/GIScreenMapper.tsx +++ b/App/common/screen/screen-mappers/GIScreenMapper.tsx @@ -3,27 +3,30 @@ import { BenefitScreen, ComparePlanScreen, MarketBenefitCompareScreen, + MigrationBenefitScreen, QuoteApologyScreen, QuoteOfferScreen, + RenewalPlanMigrationScreen, WaitingPeriodScreen, } from "../../../Container/Navi-Insurance"; import { GenericActionPayload } from "../../actions/GenericAction"; import { AnalyticsEventNameConstants, - BASE_SCREEN, BENEFIT_SCREEN, BUY_INSURANCE_SCREEN, COMPARE_PLAN_SCREEN, EVENT_NAMES, MARKET_BENEFITS_COMPARE_SCREEN, + MIGRATION_BENEFIT_SCREEN, QUOTE_APOLOGY_SCREEN, QUOTE_OFFER_SCREEN, + RENEWAL_PLAN_MIGRATION_SCREEN, WAITING_PERIOD_SCREEN, } from "../../constants"; +import { sendAsAnalyticsEvent } from "../../hooks/useAnalyticsEvent"; import { CtaData } from "../../interface"; import { ScreenData } from "../../interface/widgets/screenData/ScreenData"; import { getScreenNameFromCtaData } from "../../utilities/MiscUtils"; -import { sendAsAnalyticsEvent } from "../../hooks/useAnalyticsEvent"; export const GIScreenMapper = { getScreen( @@ -83,6 +86,22 @@ export const GIScreenMapper = { handleActions={handleActions} /> ); + case MIGRATION_BENEFIT_SCREEN: + return ( + + ); + case RENEWAL_PLAN_MIGRATION_SCREEN: + return ( + + ); default: { return ; } diff --git a/App/common/utilities/AnimationUtils.ts b/App/common/utilities/AnimationUtils.ts new file mode 100644 index 0000000000..99c17346e2 --- /dev/null +++ b/App/common/utilities/AnimationUtils.ts @@ -0,0 +1,17 @@ +import { LayoutAnimation } from "react-native"; + +export const NaviAnimationConfig = ({ duration }: { duration: number }) => ({ + duration, + update: { + type: LayoutAnimation.Types.easeInEaseOut, + property: LayoutAnimation.Properties.opacity, + }, + delete: { + type: LayoutAnimation.Types.easeInEaseOut, + property: LayoutAnimation.Properties.opacity, + }, + create: { + type: LayoutAnimation.Types.easeInEaseOut, + property: LayoutAnimation.Properties.opacity, + }, +}); diff --git a/App/common/utilities/CtaParamsUtils.ts b/App/common/utilities/CtaParamsUtils.ts index 1e10655eaa..c104b87341 100644 --- a/App/common/utilities/CtaParamsUtils.ts +++ b/App/common/utilities/CtaParamsUtils.ts @@ -1,67 +1,40 @@ -import { APPLICATION_ID, QUOTE_ID } from "../constants"; -import { CtaData, CtaParameter, ParameterType } from "../interface"; +import { APPLICATION_ID, PRE_QUOTE_ID, QUOTE_ID } from "../constants"; +import { CtaData, CtaParameter } from "../interface"; import { ScreenMetaData } from "../interface/widgets/screenData/ScreenMetaData"; -export const extractCtaParameters = ( - ctaData: any, -): { - preQuoteId?: string | undefined | null; - quoteId?: string | undefined | null; - navigatorType?: string | undefined | null; - applicationId?: string | undefined | null; - source?: string | undefined | null; - planId?: string | undefined | null; - benefitType?: string | undefined | null; - sourceScreen?: string | undefined | null; -} => { - let preQuoteId: string | undefined | null = undefined; - let quoteId: string | undefined | null = undefined; - let navigatorType: string | undefined | null = undefined; - let applicationId: string | undefined | null = undefined; - let source: string | undefined | null = undefined; - let planId: string | undefined | null = undefined; - let benefitType: string | undefined | null = undefined; - let sourceScreen: string | undefined | null = undefined; +export const extractCtaParameters = (ctaData: any) => { + const params: Record = { + preQuoteId: undefined, + quoteId: undefined, + navigatorType: undefined, + applicationId: undefined, + source: undefined, + planId: undefined, + benefitType: undefined, + sourceScreen: undefined, + toggleNextCta: undefined, + previousPlanId: undefined, + planType: undefined, + }; ctaData?.parameters?.forEach((item: CtaParameter) => { - switch (item.key) { - case ParameterType.PRE_QUOTE_ID: - preQuoteId = item.value; - break; - case ParameterType.QUOTE_ID: - quoteId = item.value; - break; - case ParameterType.NAVIGATOR_TYPE: - navigatorType = item.value; - break; - case ParameterType.APPLICATION_ID: - applicationId = item.value; - break; - case ParameterType.SOURCE: - source = item.value; - break; - case ParameterType.PLAN_ID: - planId = item.value; - break; - case ParameterType.BENEFIT_TYPE: - benefitType = item.value; - break; - case ParameterType.SOURCE_SCREEN: - sourceScreen = item.value; - break; - default: - break; + if (item.key in params) { + params[item.key] = item.value; } }); - return { - preQuoteId, - quoteId, - navigatorType, - applicationId, - source, - planId, - benefitType, - sourceScreen, + + return params as { + preQuoteId?: string | null; + quoteId?: string | null; + navigatorType?: string | null; + applicationId?: string | null; + source?: string | null; + planId?: string | null; + benefitType?: string | null; + sourceScreen?: string | null; + toggleNextCta?: boolean | null; + previousPlanId?: string | null; + planType?: string | null; }; }; @@ -70,6 +43,13 @@ export const getQuoteIdFromCta = (ctaData?: CtaData) => { return quoteObj?.value; }; +export const getPreQuoteIdFromCta = (ctaData?: CtaData) => { + const preQuoteObj = ctaData?.parameters?.find( + item => item.key === PRE_QUOTE_ID, + ); + return preQuoteObj?.value; +}; + export const getQuoteIdFromScreenMetaData = ( screenMetaData?: ScreenMetaData, ) => { @@ -79,6 +59,15 @@ export const getQuoteIdFromScreenMetaData = ( return quoteObj?.value; }; +export const getPreQuoteIdFromScreenMetaData = ( + screenMetaData?: ScreenMetaData, +) => { + const preQuoteObj = screenMetaData?.screenProperties?.find( + item => item.key === PRE_QUOTE_ID, + ); + return preQuoteObj?.value; +}; + export const getApplicationIdFromCta = (ctaData?: CtaData) => { const quoteObj = ctaData?.parameters?.find( item => item.key === APPLICATION_ID, diff --git a/App/common/utilities/MiscUtils.ts b/App/common/utilities/MiscUtils.ts index 9ad76ebb45..8a97bdf04f 100644 --- a/App/common/utilities/MiscUtils.ts +++ b/App/common/utilities/MiscUtils.ts @@ -50,7 +50,6 @@ export function updateValueByKeyPath( return; } } - const lastKey: string = keys[keys.length - 1] || ""; if (!!lastKey && currentObj.hasOwnProperty(lastKey)) { //the values here should be in string format @@ -72,3 +71,19 @@ export function updateValueByKeyPath( export const getTextWithHtmlSpace = (text?: string) => { return text?.split(" ").join(SPACE_UNICODE); }; + +export const parseString = (value: string) => { + switch (value) { + case "true": + return true; + case "false": + return false; + case "null": + case "undefined": + case "": + case "[object Object]": + return null; + default: + return value; + } +}; diff --git a/App/common/utilities/SharedPreferenceUtils.ts b/App/common/utilities/SharedPreferenceUtils.ts index 5bdc87974b..682b593279 100644 --- a/App/common/utilities/SharedPreferenceUtils.ts +++ b/App/common/utilities/SharedPreferenceUtils.ts @@ -1,11 +1,12 @@ -import { - AnalyticsEventNameConstants, - BASE_SCREEN, - EVENT_NAMES, -} from "../constants"; +import { AnalyticsEventNameConstants, EVENT_NAMES } from "../constants"; import { sendAsAnalyticsEvent } from "../hooks/useAnalyticsEvent"; import { PreferenceManagerConnector } from "../native-module/NativeModules"; +export interface PreferenceData { + shouldPersistForSession: boolean; + value: string | number | boolean; +} + export const getStringPreference = async ( key: string, type: string = "string", diff --git a/App/common/utilities/index.ts b/App/common/utilities/index.ts new file mode 100644 index 0000000000..8691854a29 --- /dev/null +++ b/App/common/utilities/index.ts @@ -0,0 +1,12 @@ +export * from "./AlfredUtils"; +export * from "./CacheUtils"; +export * from "./CtaParamsUtils"; +export * from "./ErrorUtils"; +export * from "./MiscUtils"; +export * from "./MockApiUtil"; +export * from "./NavigationUtil"; +export * from "./RecordUtils"; +export * from "./ScreenPropertiesUtils"; +export * from "./SerializerUtil"; +export * from "./SharedPreferenceUtils"; +export * from "./SizeUtils"; diff --git a/App/common/widgets/widget-actions/WidgetActionHandler.ts b/App/common/widgets/widget-actions/WidgetActionHandler.ts index 7a38868539..804fd0546f 100644 --- a/App/common/widgets/widget-actions/WidgetActionHandler.ts +++ b/App/common/widgets/widget-actions/WidgetActionHandler.ts @@ -1,8 +1,10 @@ import { Dispatch, SetStateAction } from "react"; import { SumInsuredRequestData, + handlePatchRenewalQuote, updateSumInsuredData, -} from "../../../Container/Navi-Insurance/network/QuotePageApi"; +} from "../../../Container/Navi-Insurance/network"; + import { ActionMetaData, GenericActionPayload, @@ -23,7 +25,6 @@ import { import { AnalyticsEvent, CtaData } from "../../interface"; import { ScreenData } from "../../interface/widgets/screenData/ScreenData"; import { FinalPatchCallRequestBody } from "../../interface/widgets/widgetData/FooterWithCardWidgetData"; -import { NativeDeeplinkNavigatorModule } from "../../native-module/NativeModules"; import { ScreenState } from "../../screen/BaseScreen"; import { getApplicationFromScreenMetaData, @@ -33,6 +34,7 @@ import { } from "../../utilities/CtaParamsUtils"; import { getErrorTypeFromStatusCode } from "../../utilities/ErrorUtils"; import { updateValueByKeyPath } from "../../utilities/MiscUtils"; +import { globalHandleClick } from "../../utilities/NavigationUtil"; import { parseValue } from "../../utilities/SerializerUtil"; import { WidgetActionTypes } from "./WidgetActionTypes"; @@ -183,6 +185,7 @@ const WidgetActionHandler = { setScreenData, widgetMetaData, AnalyticsMethodNameConstant.FINAL_PATCH_CALL, + AnalyticsFlowNameConstant.GI_RN_QUOTE, screenData, url, ApiMethod.PATCH, @@ -190,6 +193,18 @@ const WidgetActionHandler = { }); } + case WidgetActionTypes.PATCH_RENEWAL_QUOTE: { + handlePatchRenewalQuote({ + widgetMetaData, + ctaData, + setScreenData, + setErrorMetaData, + navigation, + screenData, + }); + return; + } + case WidgetActionTypes.SHOW_LOADER: { const updatedScreenData: ScreenData = { ...screenData, @@ -217,7 +232,6 @@ const WidgetActionHandler = { if (!actionPayloadList) { return undefined; } - let updatedActionPayload: GenericActionPayload = { ...actionPayloadList, metaData: actionPayloadList.metaData?.map(actionPayload => { @@ -256,18 +270,17 @@ const WidgetActionHandler = { }, }; -const handleResponseData = ( +export const handleResponseData = ( nextPageCta: CtaData, setScreenData: Dispatch>, screenData?: ScreenData | null, + navigation?: any, ) => { const updatedNextPageCta: CtaData = { ...nextPageCta, finish: nextPageCta.finish ? nextPageCta.finish : false, }; - NativeDeeplinkNavigatorModule.navigateToNaviDeeplinkNavigator( - JSON.stringify(updatedNextPageCta), - ); + globalHandleClick(navigation, updatedNextPageCta); setScreenData({ ...screenData, screenState: ScreenState.LOADED, @@ -275,11 +288,12 @@ const handleResponseData = ( return; }; -const handleErrorData = ( +export const handleErrorData = ( error: any, setScreenData: Dispatch>, widgetMetaData: ActionMetaData, methodName: string, + flowName: string, screenData?: ScreenData | null, apiUrl?: string, apiMethod?: string, @@ -297,7 +311,7 @@ const handleErrorData = ( source: widgetMetaData.screenName || "", statusCode: error.statusCode, moduleName: AnalyticsModuleNameConstant.GI, - flowName: AnalyticsFlowNameConstant.GI_RN_QUOTE, + flowName: flowName, methodName: methodName, globalErrorType: getErrorTypeFromStatusCode(error.statusCode || -1), isAppDowntimeEvent: false, diff --git a/App/common/widgets/widget-actions/WidgetActionTypes.ts b/App/common/widgets/widget-actions/WidgetActionTypes.ts index 621772f073..bcdc0a0dae 100644 --- a/App/common/widgets/widget-actions/WidgetActionTypes.ts +++ b/App/common/widgets/widget-actions/WidgetActionTypes.ts @@ -8,4 +8,5 @@ export const WidgetActionTypes = { SHOW_LOADER: "SHOW_LOADER", FINAL_PATCH_CALL: "FINAL_PATCH_CALL", ANALYTIC_ACTION: "ANALYTIC_ACTION", + PATCH_RENEWAL_QUOTE: "PATCH_RENEWAL_QUOTE", }; diff --git a/App/common/widgets/widgetResolver.tsx b/App/common/widgets/widgetResolver.tsx index bd41d44834..847542b4cd 100644 --- a/App/common/widgets/widgetResolver.tsx +++ b/App/common/widgets/widgetResolver.tsx @@ -3,6 +3,7 @@ import { CardWithIconWidget, CardWithListItemsWidget, ComparisonWidget, + ExpandableListWidget, FAB, FooterWithCardWidget, GridWithCardWidget, @@ -24,12 +25,14 @@ import { TitleWithColumnWidget, TitleWithHorizontalCarouselListWidget, TitleWithListWidget, + SelectCardWithFooterWidget, } from "../../../components/widgets"; import { GenericActionPayload } from "../actions/GenericAction"; import { CARD_WITH_ICON_WIDGET, CARD_WITH_LIST_ITEMS_WIDGET, COMPARISON_WIDGET, + EXPANDABLE_LIST_WIDGET, FAB_REQUEST_TO_CALLBACK, FOOTER_WITH_CARD_WIDGET, GRID_WITH_CARD_WIDGET, @@ -51,11 +54,13 @@ import { TITLE_WITH_LIST_WIDGET, TITLE_WITH_HORIZONTAL_CAROUSEL_LIST_WIDGET, SELECT_CARD_WITH_TAG_LIST_ITEMS_WIDGET, + SELECT_CARD_WITH_FOOTER_WIDGET, } from "../constants"; import { CtaData } from "../interface"; import { GenericWidgetData, Widget } from "../interface/widgets/Widget"; import { SumInsuredWidgetData } from "../interface/widgets/widgetData/SumInsuredWidgetData"; import { ScreenState } from "../screen/BaseScreen"; +import React from "react"; export const GetWidgetView = { getWidget: ( @@ -355,6 +360,28 @@ function resolveWidgetView( handleClick={handleClick} /> ); + case EXPANDABLE_LIST_WIDGET: + return ( + + ); + case SELECT_CARD_WITH_FOOTER_WIDGET: + return ( + + ); default: return ; } diff --git a/android/app/src/main/java/com/naviapp/common/navigator/NaviDeepLinkNavigator.kt b/android/app/src/main/java/com/naviapp/common/navigator/NaviDeepLinkNavigator.kt index a5abf8892b..d043076515 100644 --- a/android/app/src/main/java/com/naviapp/common/navigator/NaviDeepLinkNavigator.kt +++ b/android/app/src/main/java/com/naviapp/common/navigator/NaviDeepLinkNavigator.kt @@ -65,6 +65,7 @@ import com.navi.common.ui.activity.NaviWebViewActivity import com.navi.common.utils.Constants.ADDITIONAL_PARAMETERS import com.navi.common.utils.Constants.APP_PLATFORM_APPLICATION_TYPE import com.navi.common.utils.Constants.CLEAR_TASK +import com.navi.common.utils.Constants.CLEAR_TOP import com.navi.common.utils.Constants.CTA_URL import com.navi.common.utils.Constants.FALSE import com.navi.common.utils.Constants.KEY_CTA_DATA @@ -78,6 +79,7 @@ import com.navi.gold.navigator.NaviDigitalGoldDeeplinkNavigator import com.navi.gold.ui.IconTitleDescBottomSheet import com.navi.insurance.BuildConfig import com.navi.insurance.navigator.NaviInsuranceDeeplinkNavigator +import com.navi.insurance.util.TRUE import com.navi.insurance.util.VIDEO_ID_EXTRA import com.navi.moneymanager.common.navigation.navigator.MMDeeplinkNavigator import com.navi.moneymanager.common.navigation.navigator.MMDeeplinkNavigator.MONEY_MANAGER_ACTIVITY @@ -760,22 +762,38 @@ object NaviDeepLinkNavigator : DeepLinkListener { return } REACT_NATIVE_ACTIVITY -> { + var useClearTop = true + var intentFlags = 0 + intent = Intent(activity, ReactActivity::class.java).apply { - // By default the behaviour would have single instance of RN - // activity in stack, incase we need custom please use clearTask - // flag configurable in cta. - flags = FLAG_ACTIVITY_CLEAR_TOP - } - ctaData.parameters?.forEach { lineItem -> - when (lineItem.key) { - CLEAR_TASK -> { - if (lineItem.value?.equals(FALSE) == true) { - intent?.apply { flags = FLAG_ACTIVITY_SINGLE_TOP } + // By default, the behavior would have a single instance of RN + // activity in the stack. + // Use the clearTask flag configurable in CTA for custom behavior. + + ctaData.parameters?.forEach { lineItem -> + when (lineItem.key) { + CLEAR_TASK -> { + if (lineItem.value?.equals(FALSE) == true) { + intentFlags = FLAG_ACTIVITY_SINGLE_TOP + } + } + CLEAR_TOP -> { + useClearTop = lineItem.value?.equals(TRUE) ?: true + } } } + + // Apply the CLEAR_TOP flag if useClearTop is true and no + // conflicting flags were set + if (useClearTop && intentFlags == 0) { + intentFlags = FLAG_ACTIVITY_CLEAR_TOP + } + + // Set the final intent flags + flags = intentFlags } - } + val gson = Gson() val cta = gson.toJson(ctaData) bundle.putString(KEY_CTA_DATA, cta) diff --git a/android/app/src/main/java/com/naviapp/react_native/native_bridge/connectors/PreferenceManagerConnector.kt b/android/app/src/main/java/com/naviapp/react_native/native_bridge/connectors/PreferenceManagerConnector.kt index d157ed035f..c34d93e832 100644 --- a/android/app/src/main/java/com/naviapp/react_native/native_bridge/connectors/PreferenceManagerConnector.kt +++ b/android/app/src/main/java/com/naviapp/react_native/native_bridge/connectors/PreferenceManagerConnector.kt @@ -12,7 +12,10 @@ import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.ReactContextBaseJavaModule import com.facebook.react.bridge.ReactMethod import com.navi.base.sharedpref.PreferenceManager +import com.navi.common.utils.TemporaryStorageHelper import com.navi.insurance.sharedpref.NaviPreferenceManager +import org.json.JSONException +import org.json.JSONObject class PreferenceManagerConnector internal constructor(context: ReactApplicationContext?) : ReactContextBaseJavaModule(context) { @@ -26,6 +29,7 @@ class PreferenceManagerConnector internal constructor(context: ReactApplicationC PreferenceConstants.STRING.value -> { PreferenceManager.getSecureString(key) ?: PreferenceManager.getStringPreference(key) + ?: TemporaryStorageHelper.getCustomSessionKey(key) } PreferenceConstants.INT.value -> { PreferenceManager.getIntPreference(key) @@ -43,7 +47,22 @@ class PreferenceManagerConnector internal constructor(context: ReactApplicationC @ReactMethod fun setString(key: String, data: String?) { if (!data.isNullOrEmpty()) { - PreferenceManager.setStringPreference(key, data) + try { + val jsonObject = JSONObject(data) + val shouldPersistForSession = + jsonObject.optBoolean("shouldPersistForSession", false) + val value = jsonObject.optString("value", null) + + if (!value.isNullOrEmpty()) { + if (shouldPersistForSession) { + TemporaryStorageHelper.setCustomSessionKey(key, value) + } else { + PreferenceManager.setStringPreference(key, value) + } + } + } catch (e: JSONException) { + PreferenceManager.setStringPreference(key, data) + } } } diff --git a/android/navi-common/src/main/java/com/navi/common/utils/Constants.kt b/android/navi-common/src/main/java/com/navi/common/utils/Constants.kt index ae5f3316ce..4ad9bba196 100644 --- a/android/navi-common/src/main/java/com/navi/common/utils/Constants.kt +++ b/android/navi-common/src/main/java/com/navi/common/utils/Constants.kt @@ -21,6 +21,7 @@ object Constants { const val SUB_REDIRECT = "SUB_REDIRECT" const val NEEDS_RESULT = "NEEDS_RESULT" const val CLEAR_TASK = "CLEAR_TASK" + const val CLEAR_TOP = "CLEAR_TOP" const val REQUEST_CODE = "REQUEST_CODE" const val LOGIN_SOURCE = "LOGIN_SOURCE" const val GROOT_REFERENCE_ID = "grootReferenceId" diff --git a/android/navi-common/src/main/java/com/navi/common/utils/TemporaryStorageHelper.kt b/android/navi-common/src/main/java/com/navi/common/utils/TemporaryStorageHelper.kt index d93a0d6e42..4d61b48a56 100644 --- a/android/navi-common/src/main/java/com/navi/common/utils/TemporaryStorageHelper.kt +++ b/android/navi-common/src/main/java/com/navi/common/utils/TemporaryStorageHelper.kt @@ -36,10 +36,18 @@ object TemporaryStorageHelper { var isOkHttpRetryEnabled: Boolean = false var customDnsValue: String = "8.8.8.8" + /** + * Used to store custom key-value pairs with a scope limited to the session. For example, in + * React Native, it can track if a bottom sheet has been viewed during a session and disable it + * for the remainder of that session. + */ + var customSessionKeyMap: HashMap = HashMap() + fun clear() { apiResponse.clear() viewVisible.clear() screenDataNeedsToUpdate.clear() + customSessionKeyMap.clear() SOFT_REF_CACHE.clear() paymentsGetMethodData = null paymentsLoaderBundleData = null @@ -86,4 +94,12 @@ object TemporaryStorageHelper { fun clearResponse(screen: String) { apiResponse.remove(screen) } + + fun setCustomSessionKey(key: String, value: Any) { + customSessionKeyMap.set(key, value) + } + + fun getCustomSessionKey(key: String): Any? { + return customSessionKeyMap.get(key) + } } diff --git a/android/navi-insurance/src/main/java/com/navi/insurance/analytics/InsuranceAnalyticsConstants.kt b/android/navi-insurance/src/main/java/com/navi/insurance/analytics/InsuranceAnalyticsConstants.kt index d848848213..97b8173800 100644 --- a/android/navi-insurance/src/main/java/com/navi/insurance/analytics/InsuranceAnalyticsConstants.kt +++ b/android/navi-insurance/src/main/java/com/navi/insurance/analytics/InsuranceAnalyticsConstants.kt @@ -553,6 +553,7 @@ object InsuranceAnalyticsConstants { const val HEADER_WITH_ICON_CONTENT_FOOTER = "HEADER_WITH_ICON_CONTENT_FOOTER" const val POLICY_SELECTOR_BOTTOMSHEET = "POLICY_SELECTOR_BOTTOMSHEET" const val AUTOPAY_NUDGE_BOTTOMSHEET = "AUTOPAY_NUDGE_BOTTOMSHEET" + const val RENEWAL_PLAN_BENEFITS_NUDGE_BOTTOM_SHEET = "RENEWAL_PLAN_BENEFITS_NUDGE_BOTTOM_SHEET" const val CHECKBOX_WITH_DROPDOWN_BOTTOMSHEET = "CHECKBOX_WITH_DROPDOWN_BOTTOMSHEET" const val UITRON_BOTTOM_SHEET = "UITRON_BOTTOM_SHEET" const val ICON_WITH_LIST_BOTTOM_SHEET = "ICON_WITH_LIST_BOTTOM_SHEET" diff --git a/android/navi-insurance/src/main/java/com/navi/insurance/common/bottom_sheet/AutopayNudgeBottomsheet.kt b/android/navi-insurance/src/main/java/com/navi/insurance/common/bottom_sheet/AutopayNudgeBottomsheet.kt index 98b3de1dc7..1b5c1b2d20 100644 --- a/android/navi-insurance/src/main/java/com/navi/insurance/common/bottom_sheet/AutopayNudgeBottomsheet.kt +++ b/android/navi-insurance/src/main/java/com/navi/insurance/common/bottom_sheet/AutopayNudgeBottomsheet.kt @@ -25,7 +25,6 @@ import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width -import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState @@ -68,21 +67,19 @@ import com.navi.insurance.common.models.AutopayNudgeBottomsheetData import com.navi.insurance.common.models.BannerItem import com.navi.insurance.common.models.GiErrorMetaData import com.navi.insurance.common.models.ItemWidgetData +import com.navi.insurance.common.reusable.components.FooterSection import com.navi.insurance.databinding.LayoutClaimsStepsBottomSheetBinding import com.navi.insurance.navigator.NaviInsuranceDeeplinkNavigator import com.navi.insurance.util.CONTENT_DATA_JSON_STRING import com.navi.insurance.util.Constants import com.navi.insurance.util.pxToDp import com.navi.naviwidgets.callbacks.WidgetCallback -import com.navi.naviwidgets.composewidget.reusable.FooterButtonComposable import com.navi.naviwidgets.composewidget.reusable.colorHIBlue import com.navi.naviwidgets.composewidget.reusable.colorHIMidBlue -import com.navi.naviwidgets.composewidget.reusable.whiteColor import com.navi.naviwidgets.extensions.NaviImage import com.navi.naviwidgets.extensions.NaviTextWidgetized import com.navi.naviwidgets.extensions.getBackground import com.navi.naviwidgets.extensions.getJsonObject -import com.navi.naviwidgets.models.FooterWithCardAndSnackbarWidgetData import dagger.hilt.android.AndroidEntryPoint import javax.inject.Inject import kotlinx.coroutines.delay @@ -181,42 +178,6 @@ class AutopayNudgeBottomsheet : BaseBottomSheet(), WidgetCallback { } } - @Composable - fun FooterSection( - footerData: FooterWithCardAndSnackbarWidgetData, - widgetCallback: WidgetCallback - ) { - Column(modifier = Modifier.padding(top = 16.dp, bottom = 24.dp)) { - Row( - horizontalArrangement = Arrangement.SpaceBetween, - modifier = Modifier.fillMaxWidth().background(whiteColor).padding(horizontal = 8.dp) - ) { - val footerButtons = - listOf( - footerData.footerWithCardAndSnackbarWidgetBody?.footerButton, - footerData.footerWithCardAndSnackbarWidgetBody?.secondaryFooterButton, - ) - footerButtons.forEach { button -> - if (button?.title?.text?.isNotNull() == true) { - FooterButtonComposable( - modifier = - Modifier.weight(1f).wrapContentHeight().padding(horizontal = 8.dp), - data = button, - widgetCallback = widgetCallback - ) - } - } - } - footerData.footerWithCardAndSnackbarWidgetBody?.bottomText?.text?.let { - NaviTextWidgetized( - textFieldData = footerData.footerWithCardAndSnackbarWidgetBody?.bottomText, - modifier = Modifier.fillMaxWidth(), - widgetCallback = widgetCallback - ) - } - } - } - @Composable fun AutoMovableHorizontalPagerCarousel( items: List?, diff --git a/android/navi-insurance/src/main/java/com/navi/insurance/common/bottom_sheet/RenewalPlanBenefitsNudgeBottomSheet.kt b/android/navi-insurance/src/main/java/com/navi/insurance/common/bottom_sheet/RenewalPlanBenefitsNudgeBottomSheet.kt new file mode 100644 index 0000000000..60eab5dec3 --- /dev/null +++ b/android/navi-insurance/src/main/java/com/navi/insurance/common/bottom_sheet/RenewalPlanBenefitsNudgeBottomSheet.kt @@ -0,0 +1,221 @@ +/* + * + * * Copyright © 2024 by Navi Technologies Limited + * * All rights reserved. Strictly confidential + * + */ + +package com.navi.insurance.common.bottom_sheet + +import android.os.Bundle +import android.view.ViewStub +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.platform.rememberNestedScrollInteropConnection +import androidx.compose.ui.unit.dp +import androidx.databinding.DataBindingUtil +import com.google.gson.reflect.TypeToken +import com.navi.analytics.utils.NaviTrackEvent +import com.navi.base.model.CtaData +import com.navi.base.model.CtaType +import com.navi.base.model.NaviClickAction +import com.navi.base.utils.orFalse +import com.navi.base.utils.orZero +import com.navi.common.constants.VENDOR_NAVI_API +import com.navi.common.model.ModuleName +import com.navi.common.utils.CommonNaviAnalytics +import com.navi.common.utils.getNetworkType +import com.navi.common.utils.getScreenHeight +import com.navi.insurance.R +import com.navi.insurance.analytics.InsuranceAnalyticsConstants +import com.navi.insurance.analytics.InsuranceAnalyticsHandler +import com.navi.insurance.common.fragment.BaseBottomSheet +import com.navi.insurance.common.models.BenefitItemData +import com.navi.insurance.common.models.GiErrorMetaData +import com.navi.insurance.common.models.HeaderItemData +import com.navi.insurance.common.models.RenewalPlanBenefitsNudgeBottomSheetData +import com.navi.insurance.common.reusable.components.FooterSection +import com.navi.insurance.databinding.LayoutClaimsStepsBottomSheetBinding +import com.navi.insurance.navigator.NaviInsuranceDeeplinkNavigator +import com.navi.insurance.util.CONTENT_DATA_JSON_STRING +import com.navi.insurance.util.Constants +import com.navi.insurance.util.pxToDp +import com.navi.naviwidgets.callbacks.WidgetCallback +import com.navi.naviwidgets.extensions.NaviImage +import com.navi.naviwidgets.extensions.NaviTextWidgetized +import com.navi.naviwidgets.extensions.getBackground +import com.navi.naviwidgets.extensions.getJsonObject +import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject + +@AndroidEntryPoint +class RenewalPlanBenefitsNudgeBottomSheet : BaseBottomSheet(), WidgetCallback { + @Inject lateinit var analyticsHandler: InsuranceAnalyticsHandler + private lateinit var binding: LayoutClaimsStepsBottomSheetBinding + private val errorTracker = CommonNaviAnalytics.naviAnalytics.GiError() + + override fun setContainerView(viewStub: ViewStub) { + viewStub.layoutResource = R.layout.layout_claims_steps_bottom_sheet + binding = DataBindingUtil.getBinding(viewStub.inflate())!! + initUI() + } + + private fun initUI() { + val dataType = object : TypeToken() {}.type + val bottomSheetData = + getJsonObject( + dataType, + (arguments?.getString(CONTENT_DATA_JSON_STRING)), + onErrorOccured = { exception -> trackError(exception) } + ) + bottomSheetData?.let { data -> + NaviTrackEvent.sendEvent(data.metaData?.analyticsEventProperties, screenName) + binding.root.setContent { + setBottomSheetRadius(8f) + RenewalPlanBenefitsNudgeScreen(data, widgetCallback = this) + } + } + } + + @Composable + fun RenewalPlanBenefitsNudgeScreen( + data: RenewalPlanBenefitsNudgeBottomSheetData, + widgetCallback: WidgetCallback + ) { + Column(modifier = Modifier.fillMaxWidth()) { + data.header?.let { headerData -> HeaderSection(headerData, widgetCallback) } + Spacer(modifier = Modifier.height(16.dp)) + LazyColumn( + modifier = + Modifier.fillMaxWidth() + .heightIn(min = 0.dp, max = pxToDp((getScreenHeight() * 0.5).toInt()).dp) + .nestedScroll(rememberNestedScrollInteropConnection()) + ) { + items(data.items?.size.orZero()) { item -> + val itemData = data.items?.get(item) + itemData?.let { BenefitsItem(itemData, widgetCallback) } + } + } + Spacer(modifier = Modifier.height(10.dp)) + data.footerButton?.let { footerData -> FooterSection(footerData, widgetCallback) } + } + } + + @Composable + fun BenefitsItem(item: BenefitItemData, widgetCallback: WidgetCallback) { + Column( + modifier = + Modifier.fillMaxWidth() + .padding(start = 16.dp, end = 16.dp, bottom = 20.dp) + .getBackground(item.cardBackground), + verticalArrangement = Arrangement.Top, + horizontalAlignment = Alignment.Start + ) { + item.topTag?.text?.let { + NaviTextWidgetized(textFieldData = item.topTag, widgetCallback = widgetCallback) + } + Spacer(modifier = Modifier.height(2.dp)) + item.contentText?.text?.let { + NaviTextWidgetized( + textFieldData = item.contentText, + widgetCallback = widgetCallback, + modifier = Modifier.fillMaxWidth().padding(12.dp) + ) + } + } + } + + @Composable + fun HeaderSection(headerData: HeaderItemData, widgetCallback: WidgetCallback) { + Column( + modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp), + horizontalAlignment = Alignment.Start, + verticalArrangement = Arrangement.Top + ) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween + ) { + headerData.leftIcon?.let { leftIcon -> + NaviImage(imageFieldData = leftIcon, modifier = Modifier.size(32.dp)) + } + headerData.rightIcon?.let { rightIcon -> + NaviImage( + imageFieldData = rightIcon, + modifier = Modifier.size(24.dp), + widgetCallback = widgetCallback + ) + } + } + Spacer(modifier = Modifier.height(8.dp)) + NaviTextWidgetized( + textFieldData = headerData.title, + widgetCallback = widgetCallback, + modifier = Modifier.fillMaxWidth() + ) + } + } + + override fun onClick(naviClickAction: NaviClickAction, widgetId: String?) { + if (naviClickAction is CtaData) { + naviClickAction.analyticsEventProperties?.let { analyticsEvent -> + analyticsHandler.sendEvent(analyticsEvent, screenName) + } + val bundle = Bundle() + bundle.putParcelable(Constants.PARAMS_EXTRA, naviClickAction) + when (naviClickAction.type) { + CtaType.OPTION_SELECTED.name -> { + requestToCallbackHAndler?.onPaymentCallback(ctaData = naviClickAction) + safelyDismissDialog() + } + CtaType.DISMISS_BOTTOM_SHEET.name -> { + safelyDismissDialog() + } + else -> { + NaviInsuranceDeeplinkNavigator.navigate( + activity = activity, + ctaData = naviClickAction, + bundle = bundle, + finish = naviClickAction.finish.orFalse(), + clearTask = naviClickAction.clearTask.orFalse(), + callbackHandler = requestToCallbackHAndler + ) + safelyDismissDialog() + } + } + } + } + + private fun trackError(error: Exception) { + errorTracker.onGlobalError( + error.stackTrace.getOrNull(0).toString(), + screenName, + ModuleName.GI.name, + CommonNaviAnalytics.GLOBAL_GENERIC_ERRORS, + null, + context?.let { getNetworkType(it) }, + GiErrorMetaData.FLOW_PRE_PURCHASE, + GiErrorMetaData.GET_JSON_OBJECT, + VENDOR_NAVI_API, + isDownTime = true + ) + } + + override val screenName = InsuranceAnalyticsConstants.RENEWAL_PLAN_BENEFITS_NUDGE_BOTTOM_SHEET + + companion object { + const val TAG = InsuranceAnalyticsConstants.RENEWAL_PLAN_BENEFITS_NUDGE_BOTTOM_SHEET + } +} diff --git a/android/navi-insurance/src/main/java/com/navi/insurance/common/fragment/RenewalBackBottomSheet.kt b/android/navi-insurance/src/main/java/com/navi/insurance/common/fragment/RenewalBackBottomSheet.kt index 54207e711e..dd74a9524b 100644 --- a/android/navi-insurance/src/main/java/com/navi/insurance/common/fragment/RenewalBackBottomSheet.kt +++ b/android/navi-insurance/src/main/java/com/navi/insurance/common/fragment/RenewalBackBottomSheet.kt @@ -18,6 +18,7 @@ import com.google.gson.Gson import com.google.gson.JsonSyntaxException import com.google.gson.reflect.TypeToken import com.navi.base.model.CtaData +import com.navi.elex.font.FontWeightEnum import com.navi.insurance.R import com.navi.insurance.analytics.InsuranceAnalyticsConstants import com.navi.insurance.databinding.BottomsheetRenewalBackBinding @@ -51,10 +52,27 @@ class RenewalBackBottomSheet : BaseBottomSheet() { } private fun initUi(bottomSheetData: RenewalBackBottomSheetData) { - binding.title.setTextFieldData(TextFieldData(bottomSheetData.title)) + setBottomSheetRadius(8f) + hideRectangleStrip() + binding.title.setTextFieldData( + TextFieldData( + text = bottomSheetData.title, + font = FontWeightEnum.NAVI_BODY_DEMI_BOLD.name + ) + ) binding.description.setTextFieldData(TextFieldData(bottomSheetData.description)) - binding.leftButton.setTextFieldData(TextFieldData(bottomSheetData.leftButton?.title)) - binding.rightButton.setTextFieldData(TextFieldData(bottomSheetData.rightButton?.title)) + binding.leftButton.setTextFieldData( + TextFieldData( + text = bottomSheetData.leftButton?.title, + font = FontWeightEnum.NAVI_BODY_DEMI_BOLD.name + ) + ) + binding.rightButton.setTextFieldData( + TextFieldData( + text = bottomSheetData.rightButton?.title, + font = FontWeightEnum.NAVI_BODY_DEMI_BOLD.name + ) + ) binding.leftButton.addOnMultipleClicksHandler { handleCta(bottomSheetData.leftButton?.ctaData) diff --git a/android/navi-insurance/src/main/java/com/navi/insurance/common/models/RenewalPlanBenefitsNudgeBottomSheetData.kt b/android/navi-insurance/src/main/java/com/navi/insurance/common/models/RenewalPlanBenefitsNudgeBottomSheetData.kt new file mode 100644 index 0000000000..ec2c0016ea --- /dev/null +++ b/android/navi-insurance/src/main/java/com/navi/insurance/common/models/RenewalPlanBenefitsNudgeBottomSheetData.kt @@ -0,0 +1,34 @@ +/* + * + * * Copyright © 2024 by Navi Technologies Limited + * * All rights reserved. Strictly confidential + * + */ + +package com.navi.insurance.common.models + +import com.google.gson.annotations.SerializedName +import com.navi.design.utils.BackgroundDrawableData +import com.navi.naviwidgets.models.FooterWithCardAndSnackbarWidgetData +import com.navi.naviwidgets.models.response.ImageFieldData +import com.navi.naviwidgets.models.response.PageMetaData +import com.navi.naviwidgets.models.response.TextFieldData + +data class RenewalPlanBenefitsNudgeBottomSheetData( + @SerializedName("header") val header: HeaderItemData? = null, + @SerializedName("items") val items: List? = null, + @SerializedName("footerButton") val footerButton: FooterWithCardAndSnackbarWidgetData? = null, + @SerializedName("metaData") val metaData: PageMetaData? = null +) + +data class HeaderItemData( + @SerializedName("leftIcon") val leftIcon: ImageFieldData? = null, + @SerializedName("rightIcon") val rightIcon: ImageFieldData? = null, + @SerializedName("title") val title: TextFieldData? = null, +) + +data class BenefitItemData( + @SerializedName("cardBackground") val cardBackground: BackgroundDrawableData? = null, + @SerializedName("topTag") val topTag: TextFieldData? = null, + @SerializedName("contentText") val contentText: TextFieldData? = null, +) diff --git a/android/navi-insurance/src/main/java/com/navi/insurance/common/reusable/components/FooterSection.kt b/android/navi-insurance/src/main/java/com/navi/insurance/common/reusable/components/FooterSection.kt new file mode 100644 index 0000000000..2fe122b5ae --- /dev/null +++ b/android/navi-insurance/src/main/java/com/navi/insurance/common/reusable/components/FooterSection.kt @@ -0,0 +1,58 @@ +/* + * + * * Copyright © 2024 by Navi Technologies Limited + * * All rights reserved. Strictly confidential + * + */ + +package com.navi.insurance.common.reusable.components + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.navi.base.utils.isNotNull +import com.navi.naviwidgets.callbacks.WidgetCallback +import com.navi.naviwidgets.composewidget.reusable.FooterButtonComposable +import com.navi.naviwidgets.composewidget.reusable.whiteColor +import com.navi.naviwidgets.extensions.NaviTextWidgetized +import com.navi.naviwidgets.models.FooterWithCardAndSnackbarWidgetData + +@Composable +fun FooterSection(footerData: FooterWithCardAndSnackbarWidgetData, widgetCallback: WidgetCallback) { + Column(modifier = Modifier.padding(top = 16.dp, bottom = 24.dp)) { + Row( + horizontalArrangement = Arrangement.SpaceBetween, + modifier = Modifier.fillMaxWidth().background(whiteColor).padding(horizontal = 8.dp) + ) { + val footerButtons = + listOf( + footerData.footerWithCardAndSnackbarWidgetBody?.footerButton, + footerData.footerWithCardAndSnackbarWidgetBody?.secondaryFooterButton, + ) + footerButtons.forEach { button -> + if (button?.title?.text?.isNotNull() == true) { + FooterButtonComposable( + modifier = + Modifier.weight(1f).wrapContentHeight().padding(horizontal = 8.dp), + data = button, + widgetCallback = widgetCallback + ) + } + } + } + footerData.footerWithCardAndSnackbarWidgetBody?.bottomText?.text?.let { + NaviTextWidgetized( + textFieldData = footerData.footerWithCardAndSnackbarWidgetBody?.bottomText, + modifier = Modifier.fillMaxWidth(), + widgetCallback = widgetCallback + ) + } + } +} diff --git a/android/navi-insurance/src/main/java/com/navi/insurance/common/util/NavigationHandler.kt b/android/navi-insurance/src/main/java/com/navi/insurance/common/util/NavigationHandler.kt index 0255e0569d..9ed9573cb9 100644 --- a/android/navi-insurance/src/main/java/com/navi/insurance/common/util/NavigationHandler.kt +++ b/android/navi-insurance/src/main/java/com/navi/insurance/common/util/NavigationHandler.kt @@ -366,6 +366,8 @@ class NavigationHandler @Inject constructor(private val uiControllerUtil: UiCont const val URL_FREE_INSURANCE_LANDING_PAGE = "fi_dashboard" const val URL_FREE_INSURANCE_ADDRESS_PAGE = "fi_address" const val URL_PAYMENT_REVIEW_ACTIVITY = "payment_review_activity" + const val URL_GENERATE_RENEWAL_QUOTE = "gi/renewal/quote" + const val URL_RENEWAL_PLAN_MIGRATION = "react_native/gi/renewal_plan_migration" const val BOTTOM_SHEET = "bottom_sheet" const val FRAGMENT = "fragment" const val SHARE_IMAGE = "share_image" @@ -424,6 +426,8 @@ class NavigationHandler @Inject constructor(private val uiControllerUtil: UiCont const val POLICY_SELECTOR_BOTTOMSHEET = "policy_selector_bottomsheet" const val AHC_ODC_TABULAR_BOTTOMSHEET = "ahc_odc_tabular_bottomsheet" const val AUTOPAY_NUDGE_BOTTOMSHEET = "autopay_nudge_bottomsheet" + const val RENEWAL_PLAN_BENEFITS_NUDGE_BOTTOM_SHEET = + "renewal_plan_benefits_nudge_bottom_sheet" const val HOME_VISIT_SCREEN = "gi_home_visit" } } diff --git a/android/navi-insurance/src/main/java/com/navi/insurance/common/widgets/SectionItemsWithSliderSelectorWidget.kt b/android/navi-insurance/src/main/java/com/navi/insurance/common/widgets/SectionItemsWithSliderSelectorWidget.kt index f00055747a..4e1bb61c49 100644 --- a/android/navi-insurance/src/main/java/com/navi/insurance/common/widgets/SectionItemsWithSliderSelectorWidget.kt +++ b/android/navi-insurance/src/main/java/com/navi/insurance/common/widgets/SectionItemsWithSliderSelectorWidget.kt @@ -22,7 +22,11 @@ import com.navi.design.R as DesignR import com.navi.design.utils.getNaviDrawable import com.navi.design.utils.parseColorSafe import com.navi.insurance.R -import com.navi.insurance.common.models.* +import com.navi.insurance.common.models.AmountSlider +import com.navi.insurance.common.models.NaviWidgetData +import com.navi.insurance.common.models.SectionItemsWithSlideSelectorWidgetData +import com.navi.insurance.common.models.SectionWithSlideSelectorAmountMapItem +import com.navi.insurance.common.models.SectionWithSlideSelectorLineItem import com.navi.insurance.databinding.SectionItemWithSliderSelectorLayoutBinding import com.navi.insurance.databinding.SectionItemsWithSliderSelectorLayoutBinding import com.navi.insurance.formbase.views.AmountSliderWidget @@ -30,9 +34,9 @@ import com.navi.insurance.models.response.SectionWidgetV2StateResponse import com.navi.insurance.models.response.SectionWithItemsWidgetState import com.navi.insurance.models.response.SectionWithItemsWidgetType import com.navi.insurance.util.Constants +import com.navi.naviwidgets.extensions.setImageFieldData import com.navi.naviwidgets.extensions.setTextFieldData import com.navi.naviwidgets.models.WidgetLayoutParams -import com.navi.naviwidgets.utils.NaviWidgetIconUtils import com.navi.naviwidgets.utils.setCardProperties import com.navi.naviwidgets.utils.setWidgetLayoutParams import com.navi.naviwidgets.views.CollapsibleAmountSliderView @@ -90,9 +94,7 @@ constructor(context: Context, attrs: AttributeSet? = null) : } private fun setHeaderUI() { - viewData?.headerIcon?.iconCode?.let { - binding?.headerIcon?.setImageResource(NaviWidgetIconUtils.getIconResourceId(it)) - } + viewData?.headerIcon?.let { binding?.headerIcon?.setImageFieldData(it) } binding?.leftText?.setTextFieldData(viewData?.header?.leftText) binding?.rightText?.setTextFieldData(viewData?.header?.rightText) setEditButtonSetOnClickListener() diff --git a/android/navi-insurance/src/main/java/com/navi/insurance/formbase/renewal/repo/RenewalFormRepo.kt b/android/navi-insurance/src/main/java/com/navi/insurance/formbase/renewal/repo/RenewalFormRepo.kt index d42ab1a217..3f392b292d 100644 --- a/android/navi-insurance/src/main/java/com/navi/insurance/formbase/renewal/repo/RenewalFormRepo.kt +++ b/android/navi-insurance/src/main/java/com/navi/insurance/formbase/renewal/repo/RenewalFormRepo.kt @@ -74,7 +74,9 @@ class RenewalFormRepo @Inject constructor(private val retrofitService: RetrofitS } suspend fun generateRenewalQuote(preQuoteId: String): RepoResult { - return apiResponseCallback(retrofitService.generateRenewalQuote(preQuoteId)) + return apiResponseCallback( + retrofitService.generateRenewalQuote(preQuoteId, UpdatePreQuoteRequest()) + ) } suspend fun fetchRenewalPaymentOptions( diff --git a/android/navi-insurance/src/main/java/com/navi/insurance/formbase/renewal/vm/RenewalFormBasedFragmentVM.kt b/android/navi-insurance/src/main/java/com/navi/insurance/formbase/renewal/vm/RenewalFormBasedFragmentVM.kt index c0a5cdface..bc766963ae 100644 --- a/android/navi-insurance/src/main/java/com/navi/insurance/formbase/renewal/vm/RenewalFormBasedFragmentVM.kt +++ b/android/navi-insurance/src/main/java/com/navi/insurance/formbase/renewal/vm/RenewalFormBasedFragmentVM.kt @@ -514,21 +514,35 @@ constructor( shouldFinish: Boolean, shouldAddToBackStack: Boolean ) { - if (cta?.url == "gi/renewal/quote") { - cta.parameters - ?.firstOrNull { it.key == "preQuoteId" } - ?.let { generateRenewalQuote(it.value) } - } else { - super.performActionFromCta( - cta, - actionOwner, - cta?.url == NavigationHandler.URL_QUOTE_BENEFITS, - shouldFinish, - cta?.url == NavigationHandler.URL_REMOVED_POLICY_MEMBERS || - cta?.url == NavigationHandler.URL_QUOTE_BENEFITS || - cta?.url == NavigationHandler.URL_PAYMENT_HISTORY || - cta?.url == NavigationHandler.URL_POLICY_DOCUMENTS_LIST - ) + if (cta?.url == null) return + when (cta.url) { + NavigationHandler.URL_GENERATE_RENEWAL_QUOTE -> { + cta.parameters + ?.firstOrNull { it.key == "preQuoteId" } + ?.let { generateRenewalQuote(it.value) } + } + NavigationHandler.URL_RENEWAL_PLAN_MIGRATION -> { + viewModelScope.launch( + dispatcher.io + exceptionHandler(ApiErrorTagType.GENERATE_RENEWAL_QUOTE.value) + ) { + _quoteGenerationSharedFlow.emit( + QuoteGenerationState.Success(FormNextPageResponse()) + ) + super.performActionFromCta(cta, actionOwner, false, shouldFinish, false) + } + } + else -> { + super.performActionFromCta( + cta, + actionOwner, + cta.url == NavigationHandler.URL_QUOTE_BENEFITS, + shouldFinish, + cta.url == NavigationHandler.URL_REMOVED_POLICY_MEMBERS || + cta.url == NavigationHandler.URL_QUOTE_BENEFITS || + cta.url == NavigationHandler.URL_PAYMENT_HISTORY || + cta.url == NavigationHandler.URL_POLICY_DOCUMENTS_LIST + ) + } } } } diff --git a/android/navi-insurance/src/main/java/com/navi/insurance/models/request/UpdatePreQuoteRequest.kt b/android/navi-insurance/src/main/java/com/navi/insurance/models/request/UpdatePreQuoteRequest.kt index 4bbcb09e0a..04ff0d3778 100644 --- a/android/navi-insurance/src/main/java/com/navi/insurance/models/request/UpdatePreQuoteRequest.kt +++ b/android/navi-insurance/src/main/java/com/navi/insurance/models/request/UpdatePreQuoteRequest.kt @@ -14,5 +14,6 @@ import kotlinx.parcelize.Parcelize @Parcelize class UpdatePreQuoteRequest( @SerializedName("sumInsured") val sumInsured: Long? = null, - @SerializedName("reSelectedMemberId") val reSelectedMemberId: String? = null + @SerializedName("reSelectedMemberId") val reSelectedMemberId: String? = null, + @SerializedName("planId") val planId: String? = null ) : Parcelable diff --git a/android/navi-insurance/src/main/java/com/navi/insurance/navigator/NaviInsuranceDeeplinkNavigator.kt b/android/navi-insurance/src/main/java/com/navi/insurance/navigator/NaviInsuranceDeeplinkNavigator.kt index 00aa3d7513..c0da169d97 100644 --- a/android/navi-insurance/src/main/java/com/navi/insurance/navigator/NaviInsuranceDeeplinkNavigator.kt +++ b/android/navi-insurance/src/main/java/com/navi/insurance/navigator/NaviInsuranceDeeplinkNavigator.kt @@ -53,6 +53,7 @@ import com.navi.insurance.common.bottom_sheet.HeaderWithIconContentFooterBottomS import com.navi.insurance.common.bottom_sheet.IconWithListBottomSheet import com.navi.insurance.common.bottom_sheet.LottieWithTitleBottomSheet import com.navi.insurance.common.bottom_sheet.PolicySelectorBottomsheet +import com.navi.insurance.common.bottom_sheet.RenewalPlanBenefitsNudgeBottomSheet import com.navi.insurance.common.bottom_sheet.TIExplainerBottomSheet import com.navi.insurance.common.bottom_sheet.TabularContentBottomSheet import com.navi.insurance.common.bottom_sheet.TitleWithFooterCardBottomSheet @@ -92,6 +93,7 @@ import com.navi.insurance.common.util.NavigationHandler.Companion.ICON_WITH_LIST import com.navi.insurance.common.util.NavigationHandler.Companion.LOTTIE_WITH_TITLE_BOTTOM_SHEET import com.navi.insurance.common.util.NavigationHandler.Companion.POLICY_SELECTOR_BOTTOMSHEET import com.navi.insurance.common.util.NavigationHandler.Companion.PREMIUM_PAYMENT_BOTTOMSHEET +import com.navi.insurance.common.util.NavigationHandler.Companion.RENEWAL_PLAN_BENEFITS_NUDGE_BOTTOM_SHEET import com.navi.insurance.common.util.NavigationHandler.Companion.SELECT_POLICY_ITEM_BOTTOMSHEET import com.navi.insurance.common.util.NavigationHandler.Companion.SHARE_IMAGE import com.navi.insurance.common.util.NavigationHandler.Companion.TABULAR_CONTENT_BOTTOMSHEET @@ -755,6 +757,8 @@ object NaviInsuranceDeeplinkNavigator { Pair(AhcOdcTabularBottomsheet.TAG, AhcOdcTabularBottomsheet()) AUTOPAY_NUDGE_BOTTOMSHEET -> Pair(AutopayNudgeBottomsheet.TAG, AutopayNudgeBottomsheet()) + RENEWAL_PLAN_BENEFITS_NUDGE_BOTTOM_SHEET -> + Pair(RenewalPlanBenefitsNudgeBottomSheet.TAG, RenewalPlanBenefitsNudgeBottomSheet()) else -> null } } diff --git a/android/navi-insurance/src/main/java/com/navi/insurance/network/ApiErrorTagType.kt b/android/navi-insurance/src/main/java/com/navi/insurance/network/ApiErrorTagType.kt index 1de7dbabd7..32ec3682a5 100644 --- a/android/navi-insurance/src/main/java/com/navi/insurance/network/ApiErrorTagType.kt +++ b/android/navi-insurance/src/main/java/com/navi/insurance/network/ApiErrorTagType.kt @@ -125,6 +125,7 @@ enum class ApiErrorTagType(val value: String) { POST_RENEWAL_FORM_DATA("POST_RENEWAL_FORM_DATA"), RENEWAL_FORM_BACK_TRANSITION("RENEWAL_FORM_BACK_TRANSITION"), GENERATE_RENEWAL_QUOTE("GENERATE_RENEWAL_QUOTE"), + GENERATE_RENEWAL_QUOTE_BEFORE_LOADING_PAYMENT("GENERATE_RENEWAL_QUOTE_BEFORE_LOADING_PAYMENT"), GET_RENEWAL_PAYMENT_OPTIONS("GET_RENEWAL_PAYMENT_OPTIONS"), FETCH_GI_STATIC_PAGE_ERROR("FETCH_GI_STATIC_PAGE_ERROR"), POLICY_DATE_SYNC_ERROR("POLICY_DATE_SYNC_ERROR"), diff --git a/android/navi-insurance/src/main/java/com/navi/insurance/network/retrofit/RetrofitService.kt b/android/navi-insurance/src/main/java/com/navi/insurance/network/retrofit/RetrofitService.kt index 02207cbcb3..468f8a5fe4 100644 --- a/android/navi-insurance/src/main/java/com/navi/insurance/network/retrofit/RetrofitService.kt +++ b/android/navi-insurance/src/main/java/com/navi/insurance/network/retrofit/RetrofitService.kt @@ -1144,7 +1144,8 @@ interface RetrofitService { @POST("/renew-policy/quotes/pre-quotes/{preQuoteId}") suspend fun generateRenewalQuote( - @Path("preQuoteId") preQuoteId: String + @Path("preQuoteId") preQuoteId: String, + @Body updatePreQuoteRequest: UpdatePreQuoteRequest ): Response> @GET("/renew-policy") diff --git a/android/navi-insurance/src/main/java/com/navi/insurance/paymentreview/autopayoption/repository/PaymentReviewRepository.kt b/android/navi-insurance/src/main/java/com/navi/insurance/paymentreview/autopayoption/repository/PaymentReviewRepository.kt index 897ab8bf9d..d80bf46bd3 100644 --- a/android/navi-insurance/src/main/java/com/navi/insurance/paymentreview/autopayoption/repository/PaymentReviewRepository.kt +++ b/android/navi-insurance/src/main/java/com/navi/insurance/paymentreview/autopayoption/repository/PaymentReviewRepository.kt @@ -10,6 +10,8 @@ package com.navi.insurance.paymentreview.autopayoption.repository import com.navi.common.checkmate.model.MetricInfo import com.navi.common.network.models.RepoResult import com.navi.common.network.retrofit.ResponseCallback +import com.navi.insurance.models.request.UpdatePreQuoteRequest +import com.navi.insurance.models.response.FormNextPageResponse import com.navi.insurance.models.response.PaymentReviewResponse import com.navi.insurance.network.retrofit.RetrofitService import javax.inject.Inject @@ -31,4 +33,15 @@ class PaymentReviewRepository @Inject constructor(private val retrofitService: R ), metricInfo ) + + suspend fun generateRenewalQuote( + preQuoteId: String, + request: UpdatePreQuoteRequest, + metricInfo: MetricInfo>? = null + ): RepoResult { + return apiResponseCallback( + retrofitService.generateRenewalQuote(preQuoteId, request), + metricInfo + ) + } } diff --git a/android/navi-insurance/src/main/java/com/navi/insurance/paymentreview/autopayoption/ui/PaymentReviewFragment.kt b/android/navi-insurance/src/main/java/com/navi/insurance/paymentreview/autopayoption/ui/PaymentReviewFragment.kt index 37f7e4bd2e..fd1cef6923 100644 --- a/android/navi-insurance/src/main/java/com/navi/insurance/paymentreview/autopayoption/ui/PaymentReviewFragment.kt +++ b/android/navi-insurance/src/main/java/com/navi/insurance/paymentreview/autopayoption/ui/PaymentReviewFragment.kt @@ -86,6 +86,8 @@ class PaymentReviewFragment : GiBaseFragment(), WidgetCallback { private var rvAdapter: NaviAdapter? = null private var footerData: NaviWidgetData? = null private var id: String? = null + private var planId: String? = null + private var preQuoteId: String? = null private var paymentFlowIdentifier: String? = null private var applicationType: String? = null private var applicationId: String? = null @@ -133,6 +135,8 @@ class PaymentReviewFragment : GiBaseFragment(), WidgetCallback { isRecyclable = true ) id = arguments?.getString(ID) + planId = arguments?.getString(PLAN_ID) + preQuoteId = arguments?.getString(PRE_QUOTE_ID) applicationType = arguments?.getString(APPLICATION_TYPE_EXTRA) ?: run { activity?.intent?.getStringExtra(APPLICATION_TYPE_EXTRA) } @@ -167,8 +171,14 @@ class PaymentReviewFragment : GiBaseFragment(), WidgetCallback { binding.errorView.setProperties( object : NaviErrorPageView.Callback { override fun onRetryClick(tag: String?) { - if (tag == ApiErrorTagType.BENEFIT_EXPLAINER_DETAILS.value) { - viewModel.fetchPaymentReview(id, paymentFlowIdentifier, applicationId) + if (tag == ApiErrorTagType.PAYMENT_REVIEW_SCREEN_LOAD_ERROR.value) { + viewModel.fetchPaymentReview( + id, + paymentFlowIdentifier, + applicationId, + preQuoteId, + planId + ) } } } @@ -235,7 +245,7 @@ class PaymentReviewFragment : GiBaseFragment(), WidgetCallback { } } .launchIn(viewLifecycleOwner.lifecycleScope) - viewModel.fetchPaymentReview(id, paymentFlowIdentifier, applicationId) + viewModel.fetchPaymentReview(id, paymentFlowIdentifier, applicationId, preQuoteId, planId) } private fun handleSuccessResponse() { @@ -280,7 +290,9 @@ class PaymentReviewFragment : GiBaseFragment(), WidgetCallback { viewModel.fetchPaymentReview( id, paymentFlowIdentifier, - applicationId + applicationId, + preQuoteId, + planId ) } @@ -349,7 +361,9 @@ class PaymentReviewFragment : GiBaseFragment(), WidgetCallback { viewModel.fetchPaymentReview( id, paymentFlowIdentifier, - applicationId + applicationId, + preQuoteId, + planId ) } @@ -422,7 +436,9 @@ class PaymentReviewFragment : GiBaseFragment(), WidgetCallback { viewModel.fetchPaymentReview( id, paymentFlowIdentifier, - applicationId + applicationId, + preQuoteId, + planId ) } @@ -444,7 +460,9 @@ class PaymentReviewFragment : GiBaseFragment(), WidgetCallback { viewModel.fetchPaymentReview( id, paymentFlowIdentifier, - applicationId + applicationId, + preQuoteId, + planId ) } diff --git a/android/navi-insurance/src/main/java/com/navi/insurance/paymentreview/autopayoption/viewmodel/PaymentReviewVM.kt b/android/navi-insurance/src/main/java/com/navi/insurance/paymentreview/autopayoption/viewmodel/PaymentReviewVM.kt index 4bab896bca..d64df7b2cb 100644 --- a/android/navi-insurance/src/main/java/com/navi/insurance/paymentreview/autopayoption/viewmodel/PaymentReviewVM.kt +++ b/android/navi-insurance/src/main/java/com/navi/insurance/paymentreview/autopayoption/viewmodel/PaymentReviewVM.kt @@ -17,9 +17,13 @@ import com.navi.common.di.CoroutineDispatcherProvider import com.navi.common.network.models.isSuccessWithData import com.navi.insurance.common.models.GiErrorMetaData import com.navi.insurance.common.util.ActionHandler +import com.navi.insurance.models.request.UpdatePreQuoteRequest +import com.navi.insurance.models.response.FormNextPageResponse import com.navi.insurance.models.response.PaymentReviewResponse import com.navi.insurance.network.ApiErrorTagType import com.navi.insurance.paymentreview.autopayoption.repository.PaymentReviewRepository +import com.navi.insurance.util.ID +import com.navi.insurance.util.PaymentFlowIdentifier import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject import kotlinx.coroutines.CoroutineExceptionHandler @@ -64,44 +68,93 @@ constructor( } fun fetchPaymentReview( - id: String?, + id: String? = null, paymentFlowIdentifier: String?, - applicationId: String? = null + applicationId: String? = null, + preQuoteId: String? = null, + planId: String? = null ) { viewModelScope.launch( dispatcher.io + exceptionHandler(ApiErrorTagType.PAYMENT_REVIEW_SCREEN_LOAD_ERROR.value) ) { _paymentRequestFlow.value = ResponseState.Loading - val metricInfo = - MetricInfo.InsuranceMetricInfo( - screen = "paymentReviewScreen", - isNae = { !it.isSuccessWithData() } - ) - val response = - repository.fetchPaymentReviewInMR( - id, - paymentFlowIdentifier, - applicationId, - metricInfo - ) if ( - response.error.isNull() && - response.errors.isNullOrEmpty() && - response.data.isNotNull() + id.isNullOrEmpty() && + paymentFlowIdentifier == PaymentFlowIdentifier.ANNUAL_RENEWAL.name ) { - response.data?.let { _paymentRequestFlow.value = ResponseState.Success(it) } - } else { - _paymentRequestFlow.value = - ResponseState.Failure(ApiErrorTagType.PAYMENT_REVIEW_SCREEN_LOAD_ERROR.value) - logError( - response, - GiErrorMetaData( - ApiErrorTagType.PAYMENT_REVIEW_SCREEN_LOAD_ERROR.value, - GiErrorMetaData.FLOW_POLICY_ACTIVATION, - isDownTime = true + val renewalQuoteRequest = UpdatePreQuoteRequest(planId = planId) + val metricInfo = + MetricInfo.InsuranceMetricInfo( + screen = "paymentReviewScreen", + isNae = { !it.isSuccessWithData() } ) - ) + val response = + repository.generateRenewalQuote(preQuoteId!!, renewalQuoteRequest, metricInfo) + if ( + response.error.isNull() && + response.errors.isNullOrEmpty() && + response.data.isNotNull() + ) { + response.data?.let { + val updatedId = getParameterFromCta(response.data?.cta, ID) + getPaymentReviewScreen(updatedId, paymentFlowIdentifier, applicationId) + } + } else { + _paymentRequestFlow.value = + ResponseState.Failure( + ApiErrorTagType.GENERATE_RENEWAL_QUOTE_BEFORE_LOADING_PAYMENT.value + ) + logError( + response, + GiErrorMetaData( + ApiErrorTagType.GENERATE_RENEWAL_QUOTE_BEFORE_LOADING_PAYMENT.value, + GiErrorMetaData.FLOW_POLICY_ACTIVATION, + isDownTime = true + ) + ) + } + } else { + getPaymentReviewScreen(id, paymentFlowIdentifier, applicationId) } } } + + private suspend fun getPaymentReviewScreen( + id: String?, + paymentFlowIdentifier: String?, + applicationId: String? = null, + ) { + val metricInfo = + MetricInfo.InsuranceMetricInfo( + screen = "paymentReviewScreen", + isNae = { !it.isSuccessWithData() } + ) + val response = + repository.fetchPaymentReviewInMR(id, paymentFlowIdentifier, applicationId, metricInfo) + if ( + response.error.isNull() && response.errors.isNullOrEmpty() && response.data.isNotNull() + ) { + response.data?.let { _paymentRequestFlow.value = ResponseState.Success(it) } + } else { + _paymentRequestFlow.value = + ResponseState.Failure(ApiErrorTagType.PAYMENT_REVIEW_SCREEN_LOAD_ERROR.value) + logError( + response, + GiErrorMetaData( + ApiErrorTagType.PAYMENT_REVIEW_SCREEN_LOAD_ERROR.value, + GiErrorMetaData.FLOW_POLICY_ACTIVATION, + isDownTime = true + ) + ) + } + } + + private fun getParameterFromCta(ctaData: CtaData?, property: String?): String? { + ctaData?.parameters?.forEach { keyValue -> + if (keyValue.key == property) { + return keyValue.value + } + } + return null + } } diff --git a/android/navi-insurance/src/main/java/com/navi/insurance/util/IntentConstants.kt b/android/navi-insurance/src/main/java/com/navi/insurance/util/IntentConstants.kt index 0177190463..731ee52a8f 100644 --- a/android/navi-insurance/src/main/java/com/navi/insurance/util/IntentConstants.kt +++ b/android/navi-insurance/src/main/java/com/navi/insurance/util/IntentConstants.kt @@ -53,6 +53,8 @@ const val CONTENT_DATA_JSON_STRING = "contentDataJsonString" const val OVERRIDE_BACK_PRESS = "overrideBackPress" const val BACK_PRESS_CTA = "backPressCta" const val ID = "id" +const val PLAN_ID = "planId" +const val PRE_QUOTE_ID = "preQuoteId" const val PAYMENT_FLOW_IDENTIFIER = "paymentFlowIdentifier" const val REDIRECTION_CTA = "redirectionCta" const val RELOAD_PARENT_SCREEN = "reloadParentScreen" @@ -67,3 +69,7 @@ const val AADHAR_NUMBER_EXTRA = "aadhaarNumber" const val TXN_ID_EXTRA = "txnId" const val OTP_VALUE_EXTRA = "otpValue" const val QUERY_PARAMS = "query_params" + +enum class PaymentFlowIdentifier { + ANNUAL_RENEWAL, +} diff --git a/android/navi-insurance/src/main/res/layout/bottomsheet_renewal_back.xml b/android/navi-insurance/src/main/res/layout/bottomsheet_renewal_back.xml index 60c4a89db1..41ae7c65f4 100644 --- a/android/navi-insurance/src/main/res/layout/bottomsheet_renewal_back.xml +++ b/android/navi-insurance/src/main/res/layout/bottomsheet_renewal_back.xml @@ -39,18 +39,21 @@ app:layout_constraintTop_toBottomOf="@+id/title" tools:text="@string/please_wait_2" /> - + app:layout_constraintTop_toBottomOf="@+id/description" + app:lottie_speed="1.0" /> diff --git a/android/navi-widgets/src/main/java/com/navi/naviwidgets/adapters/CheckBoxRadioButtonCardAdapter.kt b/android/navi-widgets/src/main/java/com/navi/naviwidgets/adapters/CheckBoxRadioButtonCardAdapter.kt index 021006d050..e394f59425 100644 --- a/android/navi-widgets/src/main/java/com/navi/naviwidgets/adapters/CheckBoxRadioButtonCardAdapter.kt +++ b/android/navi-widgets/src/main/java/com/navi/naviwidgets/adapters/CheckBoxRadioButtonCardAdapter.kt @@ -70,7 +70,6 @@ class CheckBoxRadioButtonCardAdapter(private val widgetCallback: WidgetCallback? binding.radioButton.setBackgroundResource(setRadioButtonIcon(cardData.selected)) if (cardData.selected.orFalse()) { binding.cardLayout.setCardProperties(cardData.selectedCardState) - binding.cardLayout.translationZ = 16f handleTextFieldData(binding.subtitle, cardData.subTitle, widgetCallback) handleTextFieldData( binding.discountTitle, diff --git a/android/navi-widgets/src/main/java/com/navi/naviwidgets/extensions/WidgetExt.kt b/android/navi-widgets/src/main/java/com/navi/naviwidgets/extensions/WidgetExt.kt index 0404f2fa56..a546aa7f0d 100644 --- a/android/navi-widgets/src/main/java/com/navi/naviwidgets/extensions/WidgetExt.kt +++ b/android/navi-widgets/src/main/java/com/navi/naviwidgets/extensions/WidgetExt.kt @@ -383,7 +383,7 @@ fun TextView.setTextFieldData( } var drawablePadding: Int? = null data.textDrawableData?.let { textDrawableData -> - setCompoundDrawablesWithIntrinsicBounds( + val leftDrawable = if (getImageFromIconCode(textDrawableData.left?.iconCode) != -1) { drawablePadding = textDrawableData.left?.drawablePadding ContextCompat.getDrawable( @@ -392,7 +392,9 @@ fun TextView.setTextFieldData( ) } else { null - }, + } + + val topDrawable = if (getImageFromIconCode(textDrawableData.top?.iconCode) != -1) { drawablePadding = textDrawableData.top?.drawablePadding ContextCompat.getDrawable( @@ -401,7 +403,9 @@ fun TextView.setTextFieldData( ) } else { null - }, + } + + val rightDrawable = if (getImageFromIconCode(textDrawableData.right?.iconCode) != -1) { drawablePadding = textDrawableData.right?.drawablePadding ContextCompat.getDrawable( @@ -410,7 +414,9 @@ fun TextView.setTextFieldData( ) } else { null - }, + } + + val bottomDrawable = if (getImageFromIconCode(textDrawableData.bottom?.iconCode) != -1) { drawablePadding = textDrawableData.bottom?.drawablePadding ContextCompat.getDrawable( @@ -419,8 +425,79 @@ fun TextView.setTextFieldData( ) } else { null - }, - ) + } + if (!textDrawableData.left?.url.isNullOrEmpty() && leftDrawable == null) { + textDrawableData.left?.url?.let { + loadDrawableFromUrl( + context, + it, + textDrawableData.left.iconWidth, + textDrawableData.left.iconHeight + ) { drawable -> + setCompoundDrawablesWithIntrinsicBounds( + drawable, + topDrawable, + rightDrawable, + bottomDrawable + ) + } + } + } else if (!textDrawableData.right?.url.isNullOrEmpty() && rightDrawable == null) { + textDrawableData.right?.url?.let { + loadDrawableFromUrl( + context, + it, + textDrawableData.right.iconWidth, + textDrawableData.right.iconHeight + ) { drawable -> + setCompoundDrawablesWithIntrinsicBounds( + leftDrawable, + topDrawable, + drawable, + bottomDrawable + ) + } + } + } else if (!textDrawableData.top?.url.isNullOrEmpty() && topDrawable == null) { + textDrawableData.top?.url?.let { + loadDrawableFromUrl( + context, + it, + textDrawableData.top.iconWidth, + textDrawableData.top.iconHeight + ) { drawable -> + setCompoundDrawablesWithIntrinsicBounds( + leftDrawable, + drawable, + rightDrawable, + bottomDrawable + ) + } + } + } else if (!textDrawableData.bottom?.url.isNullOrEmpty() && bottomDrawable == null) { + textDrawableData.bottom?.url?.let { + loadDrawableFromUrl( + context, + it, + textDrawableData.bottom.iconWidth, + textDrawableData.bottom.iconHeight + ) { drawable -> + setCompoundDrawablesWithIntrinsicBounds( + leftDrawable, + topDrawable, + rightDrawable, + drawable + ) + } + } + } else { + setCompoundDrawablesWithIntrinsicBounds( + leftDrawable, + topDrawable, + rightDrawable, + bottomDrawable + ) + } compoundDrawablePadding = if (drawablePadding != null && drawablePadding!! >= 0) { dpToPxInInt(drawablePadding!!) diff --git a/android/navi-widgets/src/main/java/com/navi/naviwidgets/utils/Utils.kt b/android/navi-widgets/src/main/java/com/navi/naviwidgets/utils/Utils.kt index eef6c68f93..bb38333da7 100644 --- a/android/navi-widgets/src/main/java/com/navi/naviwidgets/utils/Utils.kt +++ b/android/navi-widgets/src/main/java/com/navi/naviwidgets/utils/Utils.kt @@ -11,7 +11,10 @@ import android.animation.Animator import android.app.Activity import android.content.Context import android.content.res.ColorStateList +import android.content.res.Resources +import android.graphics.Bitmap import android.graphics.Color +import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.Drawable import android.graphics.drawable.GradientDrawable import android.os.Build @@ -55,7 +58,9 @@ import com.bumptech.glide.load.DataSource import com.bumptech.glide.load.engine.DiskCacheStrategy import com.bumptech.glide.load.engine.GlideException import com.bumptech.glide.request.RequestListener +import com.bumptech.glide.request.target.CustomTarget import com.bumptech.glide.request.target.Target +import com.bumptech.glide.request.transition.Transition import com.google.android.material.card.MaterialCardView import com.google.firebase.Timestamp import com.google.gson.Gson @@ -628,6 +633,48 @@ fun loadUrlIntoImageView( } catch (e: Exception) {} } +/** + * Modified to support textDrawableData in XML. This function accepts a callback, which is invoked + * when the drawable is ready, allowing the drawable to be set in its appropriate location. + */ +fun loadDrawableFromUrl( + context: Context, + url: String, + width: Int? = null, + height: Int? = null, + onDrawableReady: (Drawable?) -> Unit +) { + Glide.with(context) + .load(url) + .diskCacheStrategy(DiskCacheStrategy.DATA) + .into( + object : CustomTarget() { + override fun onResourceReady( + resource: Drawable, + transition: Transition? + ) { + if (width == null || height == null) { + onDrawableReady(resource) + return + } + val scaledDrawable = + scaleDrawable(resource, dpToPxInInt(width), dpToPxInInt(height)) + onDrawableReady(scaledDrawable) + } + + override fun onLoadCleared(placeholder: Drawable?) { + onDrawableReady(placeholder) + } + } + ) +} + +private fun scaleDrawable(drawable: Drawable, width: Int, height: Int): Drawable { + val bitmap = (drawable as BitmapDrawable).bitmap + val scaledBitmap = Bitmap.createScaledBitmap(bitmap, width, height, true) + return BitmapDrawable(Resources.getSystem(), scaledBitmap) +} + fun setTimerText( millisUntilFinished: Long, timeLeft: AppCompatTextView, diff --git a/android/navi-widgets/src/main/res/layout/card_list_item.xml b/android/navi-widgets/src/main/res/layout/card_list_item.xml index 7dff4680d6..bd73360ab9 100644 --- a/android/navi-widgets/src/main/res/layout/card_list_item.xml +++ b/android/navi-widgets/src/main/res/layout/card_list_item.xml @@ -59,7 +59,7 @@ android:layout_marginHorizontal="@dimen/dp_16" android:layout_marginTop="@dimen/dp_18" android:layout_marginBottom="@dimen/dp_18" - android:background="@color/divider_color" + android:background="@color/border_grey_color" app:layout_constraintTop_toBottomOf="@id/items" app:layout_constraintBottom_toBottomOf="parent"/> diff --git a/android/navi-widgets/src/main/res/layout/card_with_list_items.xml b/android/navi-widgets/src/main/res/layout/card_with_list_items.xml index a66201c424..28d9da9cc8 100644 --- a/android/navi-widgets/src/main/res/layout/card_with_list_items.xml +++ b/android/navi-widgets/src/main/res/layout/card_with_list_items.xml @@ -7,7 +7,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginVertical="@dimen/dp_10" - android:background="@drawable/rounded_rectange_white_border_8dp_radius" + android:background="@drawable/bg_rounded_rectangle_grey_border_4dp_radius" android:orientation="vertical" tools:ignore="MissingClass"> diff --git a/assets/colors/colors.ts b/assets/colors/colors.ts index 43d72973e6..c622dc7275 100644 --- a/assets/colors/colors.ts +++ b/assets/colors/colors.ts @@ -15,6 +15,9 @@ const Colors = { kleinBlue: "#0036A3", transparentHex: "#00000000", footerShadowColor: "#98978933", + ghostWhite: "#F9F9FA", + lightSteelBlue: "#B0C0D9", + primaryCta: "#1F002A", }; export default Colors; diff --git a/assets/mocks/mockApiResponse.json b/assets/mocks/mockApiResponse.json index e69de29bb2..9e26dfeeb6 100644 --- a/assets/mocks/mockApiResponse.json +++ b/assets/mocks/mockApiResponse.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/components/bottomsheet/title-with-list-bottom-sheet/TitleWithListBottomSheet.tsx b/components/bottomsheet/title-with-list-bottom-sheet/TitleWithListBottomSheet.tsx index 0242a026c2..5d2fdc64d0 100644 --- a/components/bottomsheet/title-with-list-bottom-sheet/TitleWithListBottomSheet.tsx +++ b/components/bottomsheet/title-with-list-bottom-sheet/TitleWithListBottomSheet.tsx @@ -15,7 +15,7 @@ import TitleWidget from "../../widgets/title-widget/TitleWidget"; import styles from "./TitleWithListBottomSheetStyle"; const PremiumDetailsRowComponent = ({ item }: { item: KeyValueInfoData }) => { - const rowVisibility = !!item.key?.text; + const rowVisibility = (item?.isVisible ?? true) && !!item.key?.text; return rowVisibility ? ( diff --git a/components/bottomsheet/title-with-list-bottom-sheet/TitleWithListBottomSheetStyle.ts b/components/bottomsheet/title-with-list-bottom-sheet/TitleWithListBottomSheetStyle.ts index 2ac3a77c22..22b246a247 100644 --- a/components/bottomsheet/title-with-list-bottom-sheet/TitleWithListBottomSheetStyle.ts +++ b/components/bottomsheet/title-with-list-bottom-sheet/TitleWithListBottomSheetStyle.ts @@ -23,6 +23,7 @@ const styles = StyleSheet.create({ }, rowContainer: { width: "100%", + flexWrap: "wrap", flex: 1, flexDirection: "row", justifyContent: "space-between", @@ -44,6 +45,8 @@ const styles = StyleSheet.create({ }, rightContent: { + flex: 1, + flexWrap: "wrap", flexDirection: "row", justifyContent: "flex-end", alignItems: "center", diff --git a/components/bottomsheet/top-section-expandable-bottom-sheet/TopSectionExpandableBottomSheet.tsx b/components/bottomsheet/top-section-expandable-bottom-sheet/TopSectionExpandableBottomSheet.tsx index 65a1260ba0..42099c5749 100644 --- a/components/bottomsheet/top-section-expandable-bottom-sheet/TopSectionExpandableBottomSheet.tsx +++ b/components/bottomsheet/top-section-expandable-bottom-sheet/TopSectionExpandableBottomSheet.tsx @@ -101,23 +101,24 @@ const TopSectionExpandableBottomSheet = ({ {bottomSheetData?.subtitle?.text && ( )} - {bottomSheetData?.headerDescriptionText && ( - - - - - - {bottomSheetData?.headerDescriptionIcon && ( - + + - )} + + + {bottomSheetData?.headerDescriptionIcon && ( + + )} + - - )} + )} {!bottomSheetData?.headerDescriptionText && ( )} diff --git a/components/bottomsheet/top-section-expandable-bottom-sheet/TopSectionExpandableBottomSheetStyle.ts b/components/bottomsheet/top-section-expandable-bottom-sheet/TopSectionExpandableBottomSheetStyle.ts index 05bc0856f1..bfc8edc6d6 100644 --- a/components/bottomsheet/top-section-expandable-bottom-sheet/TopSectionExpandableBottomSheetStyle.ts +++ b/components/bottomsheet/top-section-expandable-bottom-sheet/TopSectionExpandableBottomSheetStyle.ts @@ -37,6 +37,7 @@ const styles = StyleSheet.create({ width: "100%", alignItems: "flex-start", paddingTop: 8, + paddingBottom: 8, }, descriptionIcon: { alignItems: "flex-end", diff --git a/components/reusable/content-widget/CommonContentWidget.tsx b/components/reusable/content-widget/CommonContentWidget.tsx index 0d37212a76..7945b0e3cf 100644 --- a/components/reusable/content-widget/CommonContentWidget.tsx +++ b/components/reusable/content-widget/CommonContentWidget.tsx @@ -5,6 +5,7 @@ const CommonContentWidgets = ({ screenData, handleActions, handleClick, + pointerEvents, }: WidgetComponentProps) => { return ( ); }; diff --git a/components/reusable/footer/CommonFooter.tsx b/components/reusable/footer/CommonFooter.tsx index e826c56caa..b5dfa90821 100644 --- a/components/reusable/footer/CommonFooter.tsx +++ b/components/reusable/footer/CommonFooter.tsx @@ -9,6 +9,7 @@ const CommonFooter = ({ screenData, handleActions, handleClick, + pointerEvents, }: WidgetComponentProps) => { if (!screenData?.screenWidgets?.footerWidgets?.length) { return null; @@ -25,6 +26,7 @@ const CommonFooter = ({ handleActions={handleActions} screenState={screenData.screenState} handleClick={handleClick} + pointerEvents={pointerEvents} /> ); diff --git a/components/reusable/gradient-border/GradientBorder.tsx b/components/reusable/gradient-border/GradientBorder.tsx index d7866258be..afdbfe9d99 100644 --- a/components/reusable/gradient-border/GradientBorder.tsx +++ b/components/reusable/gradient-border/GradientBorder.tsx @@ -1,7 +1,7 @@ import { View, ViewStyle } from "react-native"; +import { commonStyles } from "../../../App/Container/Navi-Insurance/Styles"; import { NaviLinearGradient } from "../../../App/common/hooks/useGradient"; import { GradientBorderData } from "../../../App/common/interface/components/GradientBorderData"; -import { commonStyles } from "../../../App/Container/Navi-Insurance/Styles"; const GradientBorder = ({ gradientBorderData, diff --git a/components/reusable/gradient-border/GradientBorderStyles.ts b/components/reusable/gradient-border/GradientBorderStyles.ts new file mode 100644 index 0000000000..fc1299d11c --- /dev/null +++ b/components/reusable/gradient-border/GradientBorderStyles.ts @@ -0,0 +1,9 @@ +import { StyleSheet } from "react-native"; + +const styles = StyleSheet.create({ + overflowHidden: { + overflow: "hidden", + }, +}); + +export default styles; diff --git a/components/reusable/header/CommonHeader.tsx b/components/reusable/header/CommonHeader.tsx index 3de3805ba2..016fa1d713 100644 --- a/components/reusable/header/CommonHeader.tsx +++ b/components/reusable/header/CommonHeader.tsx @@ -7,6 +7,7 @@ const CommonHeader = ({ handleActions, handleClick, style, + pointerEvents, }: WidgetComponentProps) => { return ( @@ -15,6 +16,7 @@ const CommonHeader = ({ handleActions={handleActions} screenState={screenData?.screenState} handleClick={handleClick} + pointerEvents={pointerEvents} /> ); diff --git a/components/reusable/index.ts b/components/reusable/index.ts index 599d8319e5..3ed5c0b003 100644 --- a/components/reusable/index.ts +++ b/components/reusable/index.ts @@ -1,5 +1,6 @@ -export { default as SelectButton } from "./select-button/SelectButton"; -export { default as ItemSeparator } from "./item-separator/ItemSeparator"; -export { default as CommonHeader } from "./header/CommonHeader"; -export { default as CommonFooter } from "./footer/CommonFooter"; export { default as CommonContentWidgets } from "./content-widget/CommonContentWidget"; +export { default as CommonFooter } from "./footer/CommonFooter"; +export { default as GradientBorder } from "./gradient-border/GradientBorder"; +export { default as CommonHeader } from "./header/CommonHeader"; +export { default as ItemSeparator } from "./item-separator/ItemSeparator"; +export { default as SelectButton } from "./select-button/SelectButton"; diff --git a/components/widget-view-renderer/WidgetViewRenderer.tsx b/components/widget-view-renderer/WidgetViewRenderer.tsx index 74cd742eac..191fcba225 100644 --- a/components/widget-view-renderer/WidgetViewRenderer.tsx +++ b/components/widget-view-renderer/WidgetViewRenderer.tsx @@ -7,9 +7,10 @@ const WidgetViewRenderer = ({ handleActions, screenState, handleClick, + pointerEvents, }: WidgetViewRendererProps) => { return ( - + {widgetList?.map((widget, index) => { return ( { + const [isExpanded, setIsExpanded] = useState(false); + const toggleOpen = () => { + LayoutAnimation.configureNext( + NaviAnimationConfig({ duration: BENEFIT_SECTION_ANIMATION_DURATION }), + ); + if (!isExpanded) { + widgetData?.toggleEvent && sendAsAnalyticsEvent(widgetData.toggleEvent); + } + setIsExpanded(value => !value); + }; + const throttledHandleActions = useCallback( + createThrottledHandler( + action => handleActions(null, action), + THROTTLE_DURATION, + ), + [widgetData], + ); + const dataToShow = isExpanded + ? widgetData.listItems + : widgetData?.listItems?.slice(0, 2); + return ( + + + {widgetData?.sectionTitle && ( + + )} + + + ( + { + const { cta, actions } = item || {}; + if (actions) { + throttledHandleActions(actions); + } else if (cta && handleClick) { + handleClick(cta); + } + }} + activeOpacity={1} + > + + + {item?.leftIcon && ( + + )} + {item?.leftLottie && ( + + )} + + {item?.title && } + {item?.subtitle && ( + + )} + + + {item?.rightIcon && ( + + )} + {item?.rightLottie && ( + + )} + + + )} + keyExtractor={(item, index) => item?.id || index.toString()} + ItemSeparatorComponent={() => } + contentContainerStyle={widgetData?.listStyle} + /> + {widgetData?.listItems && widgetData?.listItems?.length > 2 && ( + + {isExpanded + ? widgetData?.collapseText && ( + + ) + : widgetData?.expandText && ( + + )} + + )} + + ); +}; +export default ExpandableListWidget; diff --git a/components/widgets/expandable-list-widget/ExpandableListWidgetStyles.ts b/components/widgets/expandable-list-widget/ExpandableListWidgetStyles.ts new file mode 100644 index 0000000000..c085006434 --- /dev/null +++ b/components/widgets/expandable-list-widget/ExpandableListWidgetStyles.ts @@ -0,0 +1,51 @@ +import { StyleSheet } from "react-native"; +import Colors from "../../../assets/colors/colors"; + +const styles = StyleSheet.create({ + container: { + backgroundColor: Colors.white, + margin: 20, + borderRadius: 4, + borderColor: Colors.lightGray, + borderWidth: 1, + shadowColor: Colors.lightSteelBlue, + elevation: 6, + }, + sectionHeader: { + backgroundColor: Colors.ghostWhite, + padding: 16, + borderTopLeftRadius: 4, + borderTopRightRadius: 4, + alignItems: "flex-start", + justifyContent: "flex-start", + }, + listContainer: { + flexDirection: "row", + justifyContent: "space-between", + alignItems: "center", + paddingTop: 16, + paddingBottom: 20, + }, + rowContainer: { + flexDirection: "row", + justifyContent: "flex-start", + alignItems: "center", + gap: 0, + width: "80%", + }, + titleContainer: { + flexDirection: "column", + alignItems: "flex-start", + }, + separator: { + height: 1, + width: "100%", + backgroundColor: Colors.lightGray, + }, + expandText: { + alignItems: "flex-start", + padding: 16, + }, +}); + +export default styles; diff --git a/components/widgets/footer-with-card-widget/FooterWithCardWidget.tsx b/components/widgets/footer-with-card-widget/FooterWithCardWidget.tsx index 566fe112d0..f2e26c8a20 100644 --- a/components/widgets/footer-with-card-widget/FooterWithCardWidget.tsx +++ b/components/widgets/footer-with-card-widget/FooterWithCardWidget.tsx @@ -30,7 +30,11 @@ export const CardComponent = ({ }) => { return ( - {cardInfo?.title?.text && } + + {cardInfo?.title?.text && ( + + )} + {cardInfo?.rightTitle?.text && ( )} diff --git a/components/widgets/footer-with-card-widget/FooterWithCardWidgetStyle.ts b/components/widgets/footer-with-card-widget/FooterWithCardWidgetStyle.ts index 5307d0428f..e562c3a183 100644 --- a/components/widgets/footer-with-card-widget/FooterWithCardWidgetStyle.ts +++ b/components/widgets/footer-with-card-widget/FooterWithCardWidgetStyle.ts @@ -75,6 +75,15 @@ const styles = StyleSheet.create({ flex: 1, flexDirection: "row", }, + footerCardColumn: { + flexDirection: "column", + alignItems: "flex-start", + flex: 1, + flexBasis: "50%", + flexGrow: 1, + flexShrink: 1, + overflow: "hidden", + }, }); export default styles; diff --git a/components/widgets/index.tsx b/components/widgets/index.tsx index 12677a3478..4d4866bab3 100644 --- a/components/widgets/index.tsx +++ b/components/widgets/index.tsx @@ -24,3 +24,5 @@ export { default as ListItemWidget } from "./list-item-with-icon-widget/ListItem export { default as CardWithListItemsWidget } from "./card-with-list-items-widget/CardWithListItemsWidget"; export { default as TitleWithHorizontalCarouselListWidget } from "./title-with-horizontal-carousel-list-widget/TitleWithHorizontalCarouselListWidget"; export { default as SelectCardWithTagListItems } from "./select-card-with-tag-list-items/SelectCardWithTagListItems"; +export { default as ExpandableListWidget } from "./expandable-list-widget/ExpandableListWidget"; +export { default as SelectCardWithFooterWidget } from "./select-card-with-footer-widget/SelectCardWithFooterWidget"; diff --git a/components/widgets/select-card-with-footer-widget/SelectCardWithFooterWidget.tsx b/components/widgets/select-card-with-footer-widget/SelectCardWithFooterWidget.tsx new file mode 100644 index 0000000000..b7451cfa29 --- /dev/null +++ b/components/widgets/select-card-with-footer-widget/SelectCardWithFooterWidget.tsx @@ -0,0 +1,71 @@ +import { useEffect, useState } from "react"; +import { View } from "react-native"; +import { sendAsAnalyticsEvent } from "../../../App/common/hooks/useAnalyticsEvent"; +import { + Item, + SelectCardWithFooterWidgetProps, +} from "../../../App/common/interface/widgets/widgetData"; +import AccordionComponent from "./accordion-component/AccordionComponent"; +import SelectionCardItem from "./SelectionCardItem"; + +const SelectCardWithFooterWidget = ({ + widgetData, + widgetStyle, + handleActions, + handleClick, + widgetIndex, +}: SelectCardWithFooterWidgetProps) => { + const [selectedItem, setSelectedItem] = useState( + widgetData?.widgetMetaData?.selectedItem, + ); + const [numItems, setNumItems] = useState( + widgetData?.visibleItems != null && widgetData?.visibleItems != undefined + ? widgetData.visibleItems + : widgetData?.items?.length, + ); + + const [isAccordionVisible, setIsAccordionVisible] = useState(true); + + const handleSelect = (itemType?: string) => { + setSelectedItem(itemType); + const item = widgetData?.items?.find(item => item?.itemType === itemType); + + item?.analyticEvents?.onSelectedEvent && + sendAsAnalyticsEvent(item?.analyticEvents?.onSelectedEvent); + + if (item?.dependentWidgets) { + handleActions( + item.dependentWidgets, + widgetData?.widgetMetaData?.onValueChangeAction, + ); + } + }; + + return ( + + {widgetData?.items + ?.slice(0, numItems) + .map((item: Item) => ( + handleSelect(item?.itemType)} + /> + ))} + {isAccordionVisible && widgetData?.accordionData && ( + + )} + + ); +}; + +export default SelectCardWithFooterWidget; diff --git a/components/widgets/select-card-with-footer-widget/SelectCardWithFooterWidgetStyles.ts b/components/widgets/select-card-with-footer-widget/SelectCardWithFooterWidgetStyles.ts new file mode 100644 index 0000000000..50b0d45907 --- /dev/null +++ b/components/widgets/select-card-with-footer-widget/SelectCardWithFooterWidgetStyles.ts @@ -0,0 +1,60 @@ +import { StyleSheet } from "react-native"; +import Colors from "../../../assets/colors/colors"; + +export const styles = StyleSheet.create({ + card: { + flexDirection: "column", + alignItems: "flex-start", + backgroundColor: Colors.white, + borderRadius: 4, + padding: 16, + marginTop: 16, + flex: 1, + borderColor: Colors.lightGray, + borderWidth: 1, + position: "relative", + elevation: 5, + shadowColor: "#D1D9E6", + shadowOffset: { width: 3, height: 3 }, + shadowOpacity: 0.3, + shadowRadius: 33, + }, + selectedCard: { + borderColor: Colors.primaryCta, + backgroundColor: Colors.white, + shadowColor: Colors.black, + shadowOffset: { width: 3, height: 3 }, + shadowOpacity: 0.3, + shadowRadius: 16, + elevation: 5, + }, + headerContainer: { + flexDirection: "row", + justifyContent: "space-between", + alignItems: "center", + marginBottom: 12, + }, + rowContainer: { + flex: 1, + flexDirection: "row", + alignItems: "center", + }, + + footerRowContainer: { + marginTop: 24, + width: "100%", + justifyContent: "space-between", + flexDirection: "row", + alignItems: "center", + }, + + footerColumn: { + flexDirection: "column", + alignItems: "flex-start", + flex: 1, + flexBasis: "50%", + flexGrow: 1, + flexShrink: 1, + overflow: "hidden", + }, +}); diff --git a/components/widgets/select-card-with-footer-widget/SelectionCardItem.tsx b/components/widgets/select-card-with-footer-widget/SelectionCardItem.tsx new file mode 100644 index 0000000000..2c1a986cdc --- /dev/null +++ b/components/widgets/select-card-with-footer-widget/SelectionCardItem.tsx @@ -0,0 +1,82 @@ +import { useEffect } from "react"; +import { TouchableOpacity, View, ViewStyle } from "react-native"; +import { StyledImage, StyledText } from ".."; +import { commonStyles } from "../../../App/Container/Navi-Insurance/Styles"; +import { SelectButtonType } from "../../../App/common/constants"; +import { sendAsAnalyticsEvent } from "../../../App/common/hooks/useAnalyticsEvent"; +import { NaviLinearGradient } from "../../../App/common/hooks/useGradient"; +import { SelectionCardItemProps } from "../../../App/common/interface/widgets/widgetData"; +import { GradientBorder, SelectButton } from "../../reusable"; +import { styles } from "./SelectCardWithFooterWidgetStyles"; + +const SelectionCardItem = ({ + handleClick, + handleActions, + item, + selected, + onSelect, +}: SelectionCardItemProps) => { + useEffect(() => { + item?.analyticEvents?.onViewEvent && + sendAsAnalyticsEvent(item?.analyticEvents?.onViewEvent); + }, []); + + return ( + + + + {item?.cardImage?.url && ( + + )} + {item?.title?.text && } + + + + {item?.tagCallout?.title?.text && ( + + + + + + )} + + {item?.footerLeftTitle?.text && ( + + + + )} + + {item?.footerRightTitle?.text && ( + + )} + + + ); +}; + +export default SelectionCardItem; diff --git a/components/widgets/select-card-with-footer-widget/accordion-component/AccordionComponent.tsx b/components/widgets/select-card-with-footer-widget/accordion-component/AccordionComponent.tsx new file mode 100644 index 0000000000..6521386d7a --- /dev/null +++ b/components/widgets/select-card-with-footer-widget/accordion-component/AccordionComponent.tsx @@ -0,0 +1,97 @@ +import { useRef } from "react"; +import { + Animated, + Dimensions, + Easing, + TouchableOpacity, + View, +} from "react-native"; +import { useAnalyticsEvent } from "../../../../App/common/hooks"; +import { NaviLinearGradient } from "../../../../App/common/hooks/useGradient"; +import { AccordionProps } from "../../../../App/common/interface/widgets/widgetData"; +import { StyledImage } from "../../../StyledImage"; +import { StyledText } from "../../styled-text/StyledText"; +import styles from "./AccordionComponentStyles"; + +const { height: screenHeight } = Dimensions.get("window"); + +const AccordionComponent = ({ + widgetData, + setNumItems, + setIsAccordionVisible, + handleActions, + handleClick, +}: AccordionProps) => { + const { sendAsAnalyticsEvent } = useAnalyticsEvent(); + const animatedTranslateY = useRef(new Animated.Value(0)).current; + const animatedOpacity = useRef(new Animated.Value(1)).current; + + const expandAccordion = () => { + if (widgetData?.accordionData?.analyticsEventProperties) + sendAsAnalyticsEvent(widgetData?.accordionData?.analyticsEventProperties); + Animated.timing(animatedTranslateY, { + toValue: screenHeight, + duration: 350, + easing: Easing.out(Easing.linear), + useNativeDriver: true, + }).start(() => { + setIsAccordionVisible(false); + }); + Animated.timing(animatedOpacity, { + toValue: 0, + duration: 100, + easing: Easing.out(Easing.linear), + useNativeDriver: true, + }).start(() => { + setNumItems(widgetData?.items?.length); + }); + }; + + return ( + + + + + {widgetData?.accordionData?.title?.text && ( + + )} + + {widgetData?.accordionData?.subtitle?.text && ( + + )} + + + {widgetData?.accordionData?.rightIcon && ( + + )} + + + + + ); +}; + +export default AccordionComponent; diff --git a/components/widgets/select-card-with-footer-widget/accordion-component/AccordionComponentStyles.ts b/components/widgets/select-card-with-footer-widget/accordion-component/AccordionComponentStyles.ts new file mode 100644 index 0000000000..88603413ba --- /dev/null +++ b/components/widgets/select-card-with-footer-widget/accordion-component/AccordionComponentStyles.ts @@ -0,0 +1,17 @@ +import { StyleSheet } from "react-native"; +const styles = StyleSheet.create({ + accordionContainer: { + paddingHorizontal: 16, + flex: 1, + flexDirection: "row", + justifyContent: "space-between", + alignItems: "center", + }, + accordionContent: { + flexDirection: "column", + alignItems: "flex-start", + paddingEnd: 16, + }, +}); + +export default styles; diff --git a/network/payloads/request/MigrationBenefitScreenRequest.ts b/network/payloads/request/MigrationBenefitScreenRequest.ts new file mode 100644 index 0000000000..f984c69cc5 --- /dev/null +++ b/network/payloads/request/MigrationBenefitScreenRequest.ts @@ -0,0 +1,10 @@ +interface MigrationBenefitScreenRequestData { + inputMap?: MigrationBenefitScreenRequest | null; + screenName?: string | null; +} +interface MigrationBenefitScreenRequest { + planId?: string | null; + previousPlanId?: string | null; + preQuoteId?: string | null; + planType?: string | null; +} diff --git a/network/payloads/request/RenewalPlanMigrationScreenRequest.ts b/network/payloads/request/RenewalPlanMigrationScreenRequest.ts new file mode 100644 index 0000000000..607703f217 --- /dev/null +++ b/network/payloads/request/RenewalPlanMigrationScreenRequest.ts @@ -0,0 +1,7 @@ +interface RenewalPlanMigrationScreenRequestData { + inputMap?: RenewalPlanMigrationScreenRequest | null; + screenName?: string | null; +} +interface RenewalPlanMigrationScreenRequest { + preQuoteId?: string | null; +}