Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: [PE-550] Revamp CGN Categories/All merchants screen with new DS #5668

Merged
merged 29 commits into from
May 6, 2024
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
5f00a8e
wip cgn merchants search revamp
CrisTofani Apr 4, 2024
80293b6
missing files
CrisTofani Apr 4, 2024
d032cd8
complete revamp of categories/merchant list
CrisTofani Apr 5, 2024
39ab4f0
fixes
CrisTofani Apr 5, 2024
7c6b185
Merge branch 'master' into PE-550-categories-full-merchant-list
CrisTofani Apr 5, 2024
a49e6b1
Merge branch 'master' into PE-550-categories-full-merchant-list
CrisTofani Apr 5, 2024
b078416
fix list not refreshing
CrisTofani Apr 5, 2024
d41e29d
Merge branch 'PE-550-categories-full-merchant-list' of github.com:pag…
CrisTofani Apr 5, 2024
3a31410
removes legacy labelled item
CrisTofani Apr 10, 2024
715da7e
Merge branch 'master' into PE-550-categories-full-merchant-list
CrisTofani Apr 15, 2024
6654a27
Merge branch 'master' into PE-550-categories-full-merchant-list
CrisTofani Apr 17, 2024
9299a1b
Merge branch 'master' into PE-550-categories-full-merchant-list
Hantex9 Apr 23, 2024
08a3cdd
Merge branch 'master' into PE-550-categories-full-merchant-list
Hantex9 Apr 23, 2024
15d0c9f
Merge branch 'master' into PE-550-categories-full-merchant-list
Hantex9 Apr 23, 2024
45b7cee
Merge branch 'master' into PE-550-categories-full-merchant-list
CrisTofani Apr 24, 2024
30f2bea
Merge branch 'master' into PE-550-categories-full-merchant-list
CrisTofani Apr 29, 2024
29d6724
Merge branch 'master' into PE-550-categories-full-merchant-list
shadowsheep1 Apr 29, 2024
d18671c
Merge branch 'master' into PE-550-categories-full-merchant-list
Hantex9 Apr 30, 2024
05490fb
Merge branch 'master' into PE-550-categories-full-merchant-list
Hantex9 Apr 30, 2024
21ab1d3
Merge branch 'master' into PE-550-categories-full-merchant-list
CrisTofani May 2, 2024
bc0050c
fixes
CrisTofani May 2, 2024
10df0c6
Merge branch 'master' into PE-550-categories-full-merchant-list
CrisTofani May 2, 2024
d1d9e91
Merge branch 'master' into PE-550-categories-full-merchant-list
Hantex9 May 2, 2024
38167ed
Merge branch 'master' into PE-550-categories-full-merchant-list
Hantex9 May 3, 2024
7c5a2f6
Merge branch 'master' into PE-550-categories-full-merchant-list
Hantex9 May 5, 2024
84caca6
Merge branch 'master' into PE-550-categories-full-merchant-list
Hantex9 May 6, 2024
dd35d68
Merge branch 'master' into PE-550-categories-full-merchant-list
CrisTofani May 6, 2024
96c039b
fix selected tab ui
CrisTofani May 6, 2024
1868144
fix loading state visualization
CrisTofani May 6, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading