Skip to content

Commit

Permalink
Merge branch 'feat/UIT-198-delete-family-member' of github.com:cultuu…
Browse files Browse the repository at this point in the history
…rnet/uitpas-mobile-app into feat/UIT-198-delete-family-member
  • Loading branch information
samvanhoeyicapps committed Nov 21, 2023
2 parents 38f535a + 4047b7d commit 891677e
Show file tree
Hide file tree
Showing 18 changed files with 308 additions and 61 deletions.
3 changes: 3 additions & 0 deletions src/_components/button/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import * as Styled from './style';
export type TButtonProps = {
accessibilityHint?: string;
accessibilityLabel?: string;
backgroundColor?: ThemeColor;
centered?: boolean;
children?: ReactNode;
color?: ThemeColor;
Expand Down Expand Up @@ -37,6 +38,7 @@ const Button: FC<TButtonProps> = ({
loading,
variant = 'contained',
color,
backgroundColor,
underline = true,
fontStyle = 'bold',
inline,
Expand All @@ -57,6 +59,7 @@ const Button: FC<TButtonProps> = ({
return (
<Styled.ButtonElement
$active={isActive}
$backgroundColor={backgroundColor}
$color={color}
$inline={inline}
$radius={radius}
Expand Down
6 changes: 5 additions & 1 deletion src/_components/button/style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export const ButtonContainer = styled.View<Pick<TButtonProps, 'inline' | 'center

export const ButtonElement = styled(TouchableRipple)<{
$active: boolean;
$backgroundColor: TButtonProps['backgroundColor'];
$color: TButtonProps['color'];
$inline: TButtonProps['inline'];
$radius: TButtonProps['radius'];
Expand All @@ -23,7 +24,10 @@ export const ButtonElement = styled(TouchableRipple)<{
align-items: center;
border-radius: ${({ $inline, $radius }) => (!$radius ? '0px' : $inline ? '24px' : '16px')};
opacity: ${({ disabled }) => (disabled ? 0.4 : 1)};
background-color: ${({ $active, $variant, theme }) => {
background-color: ${({ $active, $variant, $backgroundColor, theme }) => {
if ($backgroundColor) {
return getColor($backgroundColor);
}
if ($variant === 'contained') {
return $active ? theme.palette.primary['900'] : theme.palette.primary['700'];
}
Expand Down
19 changes: 18 additions & 1 deletion src/_hooks/usePubliqApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,23 @@ export function usePubliqApi(host: ApiHost = 'uitpas') {
[defaultHeaders, apiHost],
);

const put = useCallback(
<T = unknown, MutationParams extends TMutationParams = TMutationParams>(
mutationKey: unknown[],
path: string,
options: TPostOptions<T> = {},
) => {
// eslint-disable-next-line react-hooks/rules-of-hooks
return useMutation<T, TApiError, MutationParams>({
mutationFn: async ({ body, headers }) => HttpClient.put<T>(`${apiHost}${path}`, body, { ...defaultHeaders, ...headers }),
mutationKey,
networkMode: 'offlineFirst',
...options,
});
},
[defaultHeaders, apiHost],
);

const deleteMutation = useCallback(
<T = unknown, MutationParams extends TMutationParams = TMutationParams>(
mutationKey: unknown[],
Expand Down Expand Up @@ -133,5 +150,5 @@ export function usePubliqApi(host: ApiHost = 'uitpas') {
[accessToken, defaultHeaders, apiHost],
);

return { deleteMutation, get, getInfinite, post };
return { deleteMutation, get, getInfinite, post, put };
}
25 changes: 25 additions & 0 deletions src/_routing/_components/RootStackNavigator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import Login from '../../login/Login';
import {
AddFamilyMember,
AddFamilyMemberError,
EditFamilyMember,
FamilyInformation,
FamilyOnboarding,
FamilyOverview,
Expand Down Expand Up @@ -98,10 +99,18 @@ export const RootStackNavigator = () => {
headerShown: false,
}}
/>
<RootStack.Screen
component={EditFamilyMember}
name="EditFamilyMember"
options={{
title: i18n.t('ONBOARDING.FAMILY.EDIT_MEMBER.TITLE'),
}}
/>
<RootStack.Screen
component={FamilyInformation}
name="FamilyInformation"
options={{
gestureEnabled: false,
headerRight: UserPoints,
title: i18n.t('NAVIGATION.SHOP'),
}}
Expand Down Expand Up @@ -232,6 +241,22 @@ export const RootStackNavigator = () => {
headerShown: false,
}}
/>
<RootStack.Screen
component={EditFamilyMember}
name="EditFamilyMember"
options={{
title: i18n.t('ONBOARDING.FAMILY.EDIT_MEMBER.TITLE'),
}}
/>
<RootStack.Screen
component={FamilyInformation}
name="FamilyInformation"
options={{
gestureEnabled: false,
headerRight: UserPoints,
title: i18n.t('NAVIGATION.SHOP'),
}}
/>
<RootStack.Screen
component={FamiliesOverview}
name="FamiliesOverview"
Expand Down
2 changes: 2 additions & 0 deletions src/_routing/_components/TRootStackParamList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { TFilterRewardCategory, TFilterRewardSection } from '../../shop/_hooks/u
import { TReward, TRewardType } from '../../shop/_models/reward';
import { TSearchFilters } from '../../shop/_models/searchFilters';
import { TFilterRewardSorting } from '../../shop/_queries/useGetRewards';
import { TFamilyMember } from '../../profile/_models';

export type TRootRoute = keyof TRootStackParamList;
export type TMainRoute = keyof TMainParamsList;
Expand All @@ -31,6 +32,7 @@ export type TRootStackParamList = {
About: undefined;
AddFamilyMember: undefined;
AddFamilyMemberError: { description: string };
EditFamilyMember: { member: TFamilyMember };
Error: {
gotoAfterClose?: [TRootRoute, TMainRoute] | keyof TRootStackParamList;
message: string;
Expand Down
6 changes: 6 additions & 0 deletions src/_translations/locales/nl.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@
"BACK_TO_FAMILY_OVERVIEW": "Terug naar gezinsoverzicht"
}
},
"EDIT_MEMBER": {
"TITLE": "Gezinslid wijzigen",
"DESCRIPTION": "Kies de weergave van het gezinslid",
"SAVE": "Opslaan",
"DELETE_MEMBER": "Gezinslid verwijderen"
},
"INFORMATION": {
"INFO_TITLE": "Bekijk je gezin via deze knop",
"INFO": "Hier kan je snel je gezinssamenstelling raadplegen en alle UiTPAS kaarten raadplegen.",
Expand Down
4 changes: 4 additions & 0 deletions src/_utils/imageHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import * as Avatars from '../_assets/images/avatars';

export const DEFAULT_AVATAR_NAME = 'Emoji0';

export const getValidAvatarNameOrDefault = (name: string) => {
return Avatars[name] ? name : DEFAULT_AVATAR_NAME;
};

export const getAvatarByNameOrDefault = (name: string) => {
return Avatars[name] ?? Avatars[DEFAULT_AVATAR_NAME];
};
1 change: 1 addition & 0 deletions src/onboarding/family/_queries/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './useEditFamilyMember';
export * from './useGetFamilyMembers';
export * from './useHasFamilyMembers';
export * from './useGetRegistrationToken';
Expand Down
16 changes: 16 additions & 0 deletions src/onboarding/family/_queries/useEditFamilyMember.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { TMutationParams, usePubliqApi } from '../../../_hooks/usePubliqApi';
import { useGetMe } from '../../../profile/_queries/useGetMe';

export type TEditFamilyMemberParams = TMutationParams<{
icon: string;
}>;

export const useEditFamilyMember = (familyMemberId: string) => {
const { data: user } = useGetMe();
const api = usePubliqApi();

return api.put<string, TEditFamilyMemberParams>(
['family-member-edit'],
`/passholders/${user.id}/family-members/${familyMemberId}`,
);
};
14 changes: 5 additions & 9 deletions src/onboarding/family/_queries/useRegisterFamilyMember.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
import { usePubliqApi } from '../../../_hooks/usePubliqApi';
import { Headers } from '../../../_http/HttpClient';
import { TMutationParams, usePubliqApi } from '../../../_hooks/usePubliqApi';
import { useGetMe } from '../../../profile/_queries/useGetMe';

type TRegisterFamilyMemberParams = {
body: {
icon: string;
uitpasNumber: string;
};
headers: Headers;
};
type TRegisterFamilyMemberParams = TMutationParams<{
icon: string;
uitpasNumber: string;
}>;

export const useRegisterFamilyMember = () => {
const { data: user } = useGetMe();
Expand Down
3 changes: 0 additions & 3 deletions src/onboarding/family/addFamilyMember/AddFamilyMember.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,6 @@ export const AddFamilyMember = ({ navigation }: TProps) => {
loading={registrationTokenIsLoading || registerFamilyMemberIsLoading}
onPress={handleSubmit(formData => getRegistrationToken(formData))}
/>
{registrationTokenError?.status === 403 && (
<Styled.CancelButton color="primary.700" label={t('ONBOARDING.FAMILY.ADD_MEMBER.CANCEL')} variant="outline" />
)}
</Styled.StickyFooter>
</Styled.ScreenContainer>
);
Expand Down
31 changes: 19 additions & 12 deletions src/onboarding/family/deleteFamilyMember/DeleteFamilyMember.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { ImageStyle, StyleProp } from 'react-native';
import { useNavigation } from '@react-navigation/native';

import { BlurredModal, Icon, TouchableRipple, Trans, Typography } from '../../../_components';
import { BlurredModal, Icon, Trans } from '../../../_components';
import { queryClient } from '../../../_context';
import { useDeleteFamilyMember } from '../_queries';
import * as Styled from './style';

type TProps = {
familyMemberId: string;
name: string;
style?: StyleProp<ImageStyle>;
};

const DeleteFamilyMember = ({ style, name, familyMemberId }: TProps) => {
const DeleteFamilyMember = ({ name, familyMemberId }: TProps) => {
const navigation = useNavigation();
const [isVisible, setIsVisible] = useState(false);
const { mutateAsync, isLoading } = useDeleteFamilyMember(familyMemberId);
const { t } = useTranslation();
Expand All @@ -26,29 +26,36 @@ const DeleteFamilyMember = ({ style, name, familyMemberId }: TProps) => {
await mutateAsync({});
queryClient.invalidateQueries(['family-members']);
handleToggleIsVisible();
navigation.goBack();
};

return (
<>
<TouchableRipple onPress={handleToggleIsVisible} style={style}>
<>
<Icon name="Delete" size="small" style={style} />
<Typography color="error.800">{t('ONBOARDING.FAMILY.DELETE_MEMBER.CTA')}</Typography>
</>
</TouchableRipple>
<Styled.DeleteButtonContainer>
<Icon name="Delete" />
<Styled.DeleteMemberButton
color="error.800"
fontStyle="normal"
label={t('ONBOARDING.FAMILY.EDIT_MEMBER.DELETE_MEMBER')}
onPress={handleToggleIsVisible}
underline={false}
variant="link"
/>
</Styled.DeleteButtonContainer>
<BlurredModal isVisible={isVisible} toggleIsVisible={handleToggleIsVisible}>
<Styled.Title fontStyle="bold" size="large">
{t('ONBOARDING.FAMILY.DELETE_MEMBER.CONFIRMATION_MODAL.TITLE')}
</Styled.Title>
<Trans i18nKey="ONBOARDING.FAMILY.DELETE_MEMBER.CONFIRMATION_MODAL.DESCRIPTION" values={{ name }} />
<Styled.DeleteButton
<Styled.DeleteModalButton
backgroundColor="error.700"
fontStyle="semibold"
label={t('ONBOARDING.FAMILY.DELETE_MEMBER.CONFIRMATION_MODAL.CONFIRM')}
loading={isLoading}
onPress={handleDelete}
underline={false}
/>
<Styled.CloseButton
<Styled.CloseModalButton
color="primary.700"
fontStyle="semibold"
label={t('ONBOARDING.FAMILY.DELETE_MEMBER.CONFIRMATION_MODAL.CANCEL')}
Expand Down
16 changes: 13 additions & 3 deletions src/onboarding/family/deleteFamilyMember/style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,20 @@ export const Title = styled(Typography)`
margin-bottom: 8px;
`;

export const DeleteButton = styled(Button)`
export const DeleteButtonContainer = styled.View`
flex-direction: row;
justify-content: center;
margin-top: 24px;
`;

export const DeleteMemberButton = styled(Button)`
margin-left: 8px;
`;

export const DeleteModalButton = styled(Button)`
margin-top: 32px;
background-color: ${({ theme }) => theme.palette.error[700]};
`;
export const CloseButton = styled(Button)`

export const CloseModalButton = styled(Button)`
margin-top: 16px;
`;
84 changes: 84 additions & 0 deletions src/onboarding/family/editFamilyMember/EditFamilyMember.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { FlatList } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';

import * as Avatars from '../../../_assets/images/avatars';
import { Button, Typography } from '../../../_components';
import { queryClient } from '../../../_context';
import { TMainNavigationProp, TRootStackRouteProp } from '../../../_routing';
import { applyBarcodeMask, getAvatarByNameOrDefault, getValidAvatarNameOrDefault } from '../../../_utils';
import { useEditFamilyMember } from '../_queries';
import DeleteFamilyMember from '../deleteFamilyMember/DeleteFamilyMember';
import * as Styled from './style';

type TProps = {
navigation: TMainNavigationProp<'Profile'>;
route: TRootStackRouteProp<'EditFamilyMember'>;
};

export const EditFamilyMember = ({ navigation, route }: TProps) => {
const { member } = route.params;

const [selectedAvatar, setSelectedAvatar] = useState(getValidAvatarNameOrDefault(member.icon));
const sortedAvatars = useMemo(() => {
const avatars = Object.keys(Avatars).slice();
avatars.sort((item, item2) => {
if (item.includes('Emoji') && !item2.includes('Emoji')) {
return -1;
}
if (item2.includes('Emoji') && !item.includes('Emoji')) {
return 1;
}
return item.localeCompare(item2);
});
return avatars;
}, []);
const { mutateAsync: editFamilyMember, isLoading } = useEditFamilyMember(member.passholderId);

const { bottom } = useSafeAreaInsets();
const { t } = useTranslation();

const handleSubmit = async () => {
await editFamilyMember({ body: { icon: selectedAvatar } });
queryClient.invalidateQueries(['family-members']);
navigation.goBack();
};

return (
<Styled.ScreenContainer>
<FlatList
ListFooterComponent={<DeleteFamilyMember familyMemberId={member.passholderId} name={member.firstName} />}
ListFooterComponentStyle={{ width: '100%' }}
ListHeaderComponent={
<Styled.Header>
<Typography fontStyle="bold" numberOfLines={1} size="xxlarge">
{member.firstName}
</Typography>
<Styled.UitpasNumber color="primary.700" fontStyle="bold">
{applyBarcodeMask(member.uitpasNumber)}
</Styled.UitpasNumber>
<Styled.SelectedAvatarImage resizeMode="contain" source={getAvatarByNameOrDefault(selectedAvatar)} />
<Styled.Description color="primary.700" fontStyle="semibold">
{t('ONBOARDING.FAMILY.EDIT_MEMBER.DESCRIPTION')}
</Styled.Description>
</Styled.Header>
}
columnWrapperStyle={{ padding: 8 }}
contentContainerStyle={{ alignItems: 'center', paddingHorizontal: 8 }}
data={sortedAvatars}
numColumns={5}
renderItem={({ item: avatar }) => (
<Styled.AvatarItemBorder isSelected={avatar === selectedAvatar} onPress={() => setSelectedAvatar(avatar)}>
<Styled.AvatarItemContainer>
<Styled.AvatarItemImage resizeMode="contain" source={getAvatarByNameOrDefault(avatar)} />
</Styled.AvatarItemContainer>
</Styled.AvatarItemBorder>
)}
/>
<Styled.StickyFooter style={{ marginBottom: bottom + 16 }}>
<Button label={t('ONBOARDING.FAMILY.EDIT_MEMBER.SAVE')} loading={isLoading} onPress={handleSubmit} />
</Styled.StickyFooter>
</Styled.ScreenContainer>
);
};
Loading

0 comments on commit 891677e

Please sign in to comment.