Skip to content

Commit

Permalink
feat: [PE-550] Revamp CGN Categories/All merchants screen with new DS (
Browse files Browse the repository at this point in the history
  • Loading branch information
CrisTofani authored May 6, 2024
1 parent d4eb1b6 commit bd745dd
Show file tree
Hide file tree
Showing 11 changed files with 341 additions and 239 deletions.
7 changes: 6 additions & 1 deletion locales/en/index.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2525,11 +2525,16 @@ bonus:
online: Online
places: Places
navigationTitle: Benefits
screenTitle: Select the operator and discover the benefits
screenTitle: Find the benefits
merchantsAll: All the operators
cta:
filter: Filter
categoriesList:
title: Select a category and discover the benefits
bottomSheet:
cta: How are categories ordered by?
title: How are categories ordered by?
content: To facilitate your search and improve your experience, we have organized the categories according to frequency of use.This information is not tied to your profile, but is based on aggregate data from all users who use Carta Giovani Nazionale and have consented to data processing.
filter:
title: Filter operators
searchTitle: Find by name
Expand Down
7 changes: 6 additions & 1 deletion locales/it/index.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2525,11 +2525,16 @@ bonus:
online: Online
places: Luoghi
navigationTitle: Agevolazioni
screenTitle: Seleziona un operatore e scopri agevolazioni e opportunità
screenTitle: Scopri le proposte
merchantsAll: Tutti gli operatori
cta:
filter: Filtra
categoriesList:
title: Seleziona una categoria e scopri agevolazioni e opportunità
bottomSheet:
cta: Come vengono ordinate le categorie?
title: Come vengono ordinate le categorie?
content: Per agevolare la ricerca e migliorare la tua esperienza, abbiamo organizzato le categorie in base alla frequenza di utilizzo.Questa informazione non è legata al tuo profilo, ma si basa su dati complessivi di tutti gli utenti che utilizzano Carta Giovani Nazionale e hanno acconsentito al trattamento dei dati.
filter:
title: Filtra esercenti
searchTitle: Cerca per nome
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import * as React from "react";
import { View, FlatList, ListRenderItemInfo, Platform } from "react-native";
import { Badge, H6, ListItemNav } from "@pagopa/io-app-design-system";
import { IOStyles } from "../../../../../components/core/variables/IOStyles";
import ItemSeparatorComponent from "../../../../../components/ItemSeparatorComponent";
import { EdgeBorderComponent } from "../../../../../components/screens/EdgeBorderComponent";
import { OfflineMerchant } from "../../../../../../definitions/cgn/merchants/OfflineMerchant";
import { OnlineMerchant } from "../../../../../../definitions/cgn/merchants/OnlineMerchant";
import { Merchant } from "../../../../../../definitions/cgn/merchants/Merchant";
import CgnMerchantListItem from "./CgnMerchantListItem";
import I18n from "../../../../../i18n";

type Props = {
merchantList: ReadonlyArray<OfflineMerchant | OnlineMerchant>;
Expand All @@ -20,12 +21,29 @@ const CgnMerchantsListView: React.FunctionComponent<Props> = (props: Props) => {
const renderListItem = (
listItem: ListRenderItemInfo<OfflineMerchant | OnlineMerchant>
) => (
<CgnMerchantListItem
categories={listItem.item.productCategories}
name={listItem.item.name}
onPress={() => props.onItemPress(listItem.item.id)}
isNew={listItem.item.newDiscounts}
/>
<>
<ListItemNav
onPress={() => props.onItemPress(listItem.item.id)}
value={
listItem.item.newDiscounts ? (
<View style={IOStyles.rowSpaceBetween}>
<H6 style={{ flexGrow: 1, flexShrink: 1 }}>
{listItem.item.name}
</H6>
<View style={{ alignSelf: "center" }}>
<Badge
variant="purple"
text={I18n.t("bonus.cgn.merchantsList.news")}
/>
</View>
</View>
) : (
listItem.item.name
)
}
accessibilityLabel={listItem.item.name}
/>
</>
);

return (
Expand Down
11 changes: 0 additions & 11 deletions ts/features/bonus/cgn/navigation/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,17 +106,6 @@ export const navigateToCgnDetails = () =>
);

// Merchants
/**
* @deprecated Do not use this method when you have access to a navigation prop or useNavigation since it will behave differently,
* and many helper methods specific to screens won't be available.
*/
export const navigateToCgnMerchantsList = () =>
NavigationService.dispatchNavigationAction(
CommonActions.navigate(CGN_ROUTES.DETAILS.MAIN, {
screen: CGN_ROUTES.DETAILS.MERCHANTS.LIST
})
);

/**
* @deprecated Do not use this method when you have access to a navigation prop or useNavigation since it will behave differently,
* and many helper methods specific to screens won't be available.
Expand Down
12 changes: 6 additions & 6 deletions ts/features/bonus/cgn/navigation/navigator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import CgnMerchantDetailScreen from "../screens/merchants/CgnMerchantDetailScree
import CgnMerchantLandingWebview from "../screens/merchants/CgnMerchantLandingWebview";
import CgnMerchantsCategoriesSelectionScreen from "../screens/merchants/CgnMerchantsCategoriesSelectionScreen";
import CgnMerchantsListByCategory from "../screens/merchants/CgnMerchantsListByCategory";
import MerchantsListScreen from "../screens/merchants/CgnMerchantsListScreen";
import CgnMerchantsTabsScreen from "../screens/merchants/CgnMerchantsTabsScreen";
import { AppParamsList } from "../../../../navigation/params/AppParamsList";
import {
Expand Down Expand Up @@ -93,34 +92,35 @@ const DetailStack = createStackNavigator<CgnDetailsParamsList>();
export const CgnDetailsNavigator = () => (
<DetailStack.Navigator
initialRouteName={CGN_ROUTES.DETAILS.DETAILS}
screenOptions={{ gestureEnabled: isGestureEnabled, headerShown: false }}
screenOptions={{ gestureEnabled: isGestureEnabled, headerMode: "screen" }}
>
<DetailStack.Screen
name={CGN_ROUTES.DETAILS.DETAILS}
options={{ headerShown: false }}
component={CgnDetailScreen}
/>
<DetailStack.Screen
name={CGN_ROUTES.DETAILS.MERCHANTS.CATEGORIES}
component={CgnMerchantsCategoriesSelectionScreen}
/>
<DetailStack.Screen
name={CGN_ROUTES.DETAILS.MERCHANTS.LIST}
component={MerchantsListScreen}
/>
<DetailStack.Screen
name={CGN_ROUTES.DETAILS.MERCHANTS.LIST_BY_CATEGORY}
options={{ headerShown: false }}
component={CgnMerchantsListByCategory}
/>
<DetailStack.Screen
name={CGN_ROUTES.DETAILS.MERCHANTS.TABS}
options={{ headerShown: false }}
component={CgnMerchantsTabsScreen}
/>
<DetailStack.Screen
name={CGN_ROUTES.DETAILS.MERCHANTS.DETAIL}
options={{ headerShown: false }}
component={CgnMerchantDetailScreen}
/>
<DetailStack.Screen
name={CGN_ROUTES.DETAILS.MERCHANTS.LANDING_WEBVIEW}
options={{ headerShown: false }}
component={CgnMerchantLandingWebview}
/>
</DetailStack.Navigator>
Expand Down
1 change: 0 additions & 1 deletion ts/features/bonus/cgn/navigation/params.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ export type CgnActivationParamsList = {
export type CgnDetailsParamsList = {
[CGN_ROUTES.DETAILS.DETAILS]: undefined;
[CGN_ROUTES.DETAILS.MERCHANTS.CATEGORIES]: undefined;
[CGN_ROUTES.DETAILS.MERCHANTS.LIST]: undefined;
[CGN_ROUTES.DETAILS.MERCHANTS
.LIST_BY_CATEGORY]: CgnMerchantListByCategoryScreenNavigationParams;
[CGN_ROUTES.DETAILS.MERCHANTS.TABS]: undefined;
Expand Down
1 change: 0 additions & 1 deletion ts/features/bonus/cgn/navigation/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ const CGN_ROUTES = {
DETAILS: "CGN_DETAILS",
MERCHANTS: {
CATEGORIES: "CGN_MERCHANTS_CATEGORIES",
LIST: "CGN_MERCHANTS_LIST",
LIST_BY_CATEGORY: "CGN_MERCHANTS_LIST_BY_CATEGORY",
TABS: "CGN_MERCHANTS_TABS",
DETAIL: "CGN_MERCHANTS_DETAIL",
Expand Down
6 changes: 1 addition & 5 deletions ts/features/bonus/cgn/screens/CgnDetailScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,7 @@ import CgnOwnershipInformation from "../components/detail/CgnOwnershipInformatio
import CgnStatusDetail from "../components/detail/CgnStatusDetail";
import CgnUnsubscribe from "../components/detail/CgnUnsubscribe";
import EycaDetailComponent from "../components/detail/eyca/EycaDetailComponent";
import {
navigateToCgnMerchantsList,
navigateToCgnMerchantsTabs
} from "../navigation/actions";
import { navigateToCgnMerchantsTabs } from "../navigation/actions";
import { CgnDetailsParamsList } from "../navigation/params";
import CGN_ROUTES from "../navigation/routes";
import { cgnDetails } from "../store/actions/details";
Expand Down Expand Up @@ -220,7 +217,6 @@ const mapDispatchToProps = (dispatch: Dispatch) => ({
goBack: () => navigateBack(),
loadEycaDetails: () => dispatch(cgnEycaStatus.request()),
loadCgnDetails: () => dispatch(cgnDetails.request()),
navigateToMerchantsList: () => navigateToCgnMerchantsList(),
navigateToMerchantsTabs: () => navigateToCgnMerchantsTabs()
});

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
import * as pot from "@pagopa/ts-commons/lib/pot";
import {
Badge,
Body,
H6,
IOStyles,
IOToast,
ListItemAction,
ListItemNav
} from "@pagopa/io-app-design-system";
import * as React from "react";
import { FlatList, RefreshControl, View } from "react-native";
import { pipe } from "fp-ts/lib/function";
import * as O from "fp-ts/lib/Option";
import { useNavigation } from "@react-navigation/native";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { ProductCategoryWithNewDiscountsCount } from "../../../../../../definitions/cgn/merchants/ProductCategoryWithNewDiscountsCount";
import { getCategorySpecs } from "../../utils/filters";
import I18n from "../../../../../i18n";
import { IOStackNavigationProp } from "../../../../../navigation/params/AppParamsList";
import { CgnDetailsParamsList } from "../../navigation/params";
import { useIODispatch, useIOSelector } from "../../../../../store/hooks";
import { cgnCategoriesListSelector } from "../../store/reducers/categories";
import { cgnCategories } from "../../store/actions/categories";
import CGN_ROUTES from "../../navigation/routes";
import { useIOBottomSheetAutoresizableModal } from "../../../../../utils/hooks/bottomSheet";

export const CgnMerchantCategoriesListScreen = () => {
const insets = useSafeAreaInsets();
const isFirstRender = React.useRef<boolean>(true);
const dispatch = useIODispatch();
const potCategories = useIOSelector(cgnCategoriesListSelector);
const navigation =
useNavigation<
IOStackNavigationProp<CgnDetailsParamsList, "CGN_MERCHANTS_CATEGORIES">
>();

const { present, bottomSheet } = useIOBottomSheetAutoresizableModal({
fullScreen: true,
title: I18n.t("bonus.cgn.merchantsList.categoriesList.bottomSheet.title"),
component: (
<View style={{ paddingBottom: insets.bottom }}>
<Body>
{I18n.t("bonus.cgn.merchantsList.categoriesList.bottomSheet.content")}
</Body>
</View>
)
});

const loadCategories = () => {
dispatch(cgnCategories.request());
};
React.useEffect(loadCategories, [dispatch]);

const isError = React.useMemo(
() => pot.isError(potCategories),
[potCategories]
);

React.useEffect(() => {
if (!isFirstRender.current && isError) {
IOToast.error(I18n.t("global.genericError"));
}
// eslint-disable-next-line functional/immutable-data
isFirstRender.current = false;
}, [isError]);

const renderCategoryElement = (
category: ProductCategoryWithNewDiscountsCount,
i: number
) => {
const specs = getCategorySpecs(category.productCategory);
const countAvailable = category.newDiscounts > 0;
return pipe(
specs,
O.fold(
() => null,
s => (
<ListItemNav
key={i}
value={
countAvailable ? (
<View style={IOStyles.rowSpaceBetween}>
<H6>{I18n.t(s.nameKey)}</H6>
<Badge text={`${category.newDiscounts}`} variant="purple" />
</View>
) : (
I18n.t(s.nameKey)
)
}
accessibilityLabel={I18n.t(s.nameKey)}
onPress={() => {
navigation.navigate(
CGN_ROUTES.DETAILS.MERCHANTS.LIST_BY_CATEGORY,
{
category: s.type
}
);
}}
icon={s.icon}
/>
)
)
);
};

const categoriesToArray: ReadonlyArray<ProductCategoryWithNewDiscountsCount> =
React.useMemo(
() => [...(pot.isSome(potCategories) ? potCategories.value : [])],
[potCategories]
);

return (
<>
{bottomSheet}
<FlatList
data={categoriesToArray}
style={[
IOStyles.horizontalContentPadding,
IOStyles.flex,
{ paddingBottom: insets.bottom }
]}
keyExtractor={pc => pc.productCategory}
renderItem={({ item, index }) => renderCategoryElement(item, index)}
refreshControl={
<RefreshControl
refreshing={pot.isLoading(potCategories)}
onRefresh={loadCategories}
/>
}
ListFooterComponent={
<ListItemAction
onPress={present}
accessibilityLabel={I18n.t(
"bonus.cgn.merchantsList.categoriesList.bottomSheet.cta"
)}
label={I18n.t(
"bonus.cgn.merchantsList.categoriesList.bottomSheet.cta"
)}
variant="primary"
/>
}
/>
</>
);
};
Loading

0 comments on commit bd745dd

Please sign in to comment.