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

[Payment card / Subscription] Integrate “Subscription settings” section with backend data #43248

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
5ae57d9
feat: subscription settings ui
MrMuzyk Jun 3, 2024
af7b6df
Merge branch 'main' into feat/subsription-settings-ui
MrMuzyk Jun 3, 2024
7881f60
feat: disable auto renew page
MrMuzyk Jun 3, 2024
2f96052
fix: translations and cut corporate karma
MrMuzyk Jun 3, 2024
7da4c58
fix: cr fixes
MrMuzyk Jun 3, 2024
690d095
fix: translations
MrMuzyk Jun 3, 2024
b2c9f6f
Merge branch 'main' of https://github.com/Expensify/App into feat/sub…
MrMuzyk Jun 4, 2024
2ca5e3c
fix: remove memo
MrMuzyk Jun 4, 2024
a5ad54a
fix: whitespaces before comments
MrMuzyk Jun 4, 2024
06b74cc
fix: cr fixes
MrMuzyk Jun 4, 2024
f866282
fix: add new const for feedback survey options
MrMuzyk Jun 4, 2024
8139378
Merge branch 'main' of https://github.com/Expensify/App into feat/sub…
MrMuzyk Jun 4, 2024
e72b647
Merge branch 'main' of https://github.com/Expensify/App into feat/sub…
MrMuzyk Jun 5, 2024
09b9538
fix: cr fixes
MrMuzyk Jun 5, 2024
9157519
Merge branch 'main' of https://github.com/Expensify/App into feat/sub…
MrMuzyk Jun 6, 2024
4d7d132
fix: adjusted section displaying
MrMuzyk Jun 6, 2024
80b5c36
display values from API in subscription settings section
JKobrynski Jun 6, 2024
11add28
Merge branch 'main' of https://github.com/Expensify/App into feat/sub…
MrMuzyk Jun 7, 2024
90d8701
Merge branch 'main' into integrateSubscriptionSettingsWithBackend
JKobrynski Jun 7, 2024
f3cba59
fix: small cr fixes
MrMuzyk Jun 7, 2024
0214f78
Merge branch 'feat/subsription-settings-ui' into integrateSubscriptio…
JKobrynski Jun 7, 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
22 changes: 22 additions & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4815,6 +4815,28 @@ const CONST = {
},

SUBSCRIPTION_SIZE_LIMIT: 20000,
SUBSCRIPTION_POSSIBLE_COST_SAVINGS: {
COLLECT_PLAN: 10,
CONTROL_PLAN: 18,
},
FEEDBACK_SURVEY_OPTIONS: {
TOO_LIMITED: {
ID: 'tooLimited',
TRANSLATION_KEY: 'feedbackSurvey.tooLimited',
},
TOO_EXPENSIVE: {
ID: 'tooExpensive',
TRANSLATION_KEY: 'feedbackSurvey.tooExpensive',
},
INADEQUATE_SUPPORT: {
ID: 'inadequateSupport',
TRANSLATION_KEY: 'feedbackSurvey.inadequateSupport',
},
BUSINESS_CLOSING: {
ID: 'businessClosing',
TRANSLATION_KEY: 'feedbackSurvey.businessClosing',
},
},
} as const;

type Country = keyof typeof CONST.ALL_COUNTRIES;
Expand Down
1 change: 1 addition & 0 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ const ROUTES = {
SETTINGS_SUBSCRIPTION: 'settings/subscription',
SETTINGS_SUBSCRIPTION_SIZE: 'settings/subscription/subscription-size',
SETTINGS_SUBSCRIPTION_ADD_PAYMENT_CARD: 'settings/subscription/add-payment-card',
SETTINGS_SUBSCRIPTION_DISABLE_AUTO_RENEW_SURVEY: 'settings/subscription/disable-auto-renew-survey',
SETTINGS_PRIORITY_MODE: 'settings/preferences/priority-mode',
SETTINGS_LANGUAGE: 'settings/preferences/language',
SETTINGS_THEME: 'settings/preferences/theme',
Expand Down
1 change: 1 addition & 0 deletions src/SCREENS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ const SCREENS = {
ROOT: 'Settings_Subscription',
SIZE: 'Settings_Subscription_Size',
ADD_PAYMENT_CARD: 'Settings_Subscription_Add_Payment_Card',
DISABLE_AUTO_RENEW_SURVEY: 'Settings_Subscription_DisableAutoRenewSurvey',
},
},
SAVE_THE_WORLD: {
Expand Down
90 changes: 90 additions & 0 deletions src/components/FeedbackSurvey.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import React, {useState} from 'react';
import type {StyleProp, ViewStyle} from 'react-native';
import {View} from 'react-native';
import useLocalize from '@hooks/useLocalize';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import CONST from '@src/CONST';
import type {TranslationPaths} from '@src/languages/types';
import FixedFooter from './FixedFooter';
import FormAlertWithSubmitButton from './FormAlertWithSubmitButton';
import SingleOptionSelector from './SingleOptionSelector';
import Text from './Text';

type FeedbackSurveyProps = {
/** Title of the survey */
title: string;

/** Description of the survey */
description: string;

/** Callback to be called when the survey is submitted */
onSubmit: (reason: Option) => void;

/** Styles for the option row element */
optionRowStyles?: StyleProp<ViewStyle>;
};

type Option = {
key: string;
label: TranslationPaths;
};

const OPTIONS: Option[] = [
{key: CONST.FEEDBACK_SURVEY_OPTIONS.TOO_LIMITED.ID, label: CONST.FEEDBACK_SURVEY_OPTIONS.TOO_LIMITED.TRANSLATION_KEY},
{key: CONST.FEEDBACK_SURVEY_OPTIONS.TOO_EXPENSIVE.ID, label: CONST.FEEDBACK_SURVEY_OPTIONS.TOO_EXPENSIVE.TRANSLATION_KEY},
{key: CONST.FEEDBACK_SURVEY_OPTIONS.INADEQUATE_SUPPORT.ID, label: CONST.FEEDBACK_SURVEY_OPTIONS.INADEQUATE_SUPPORT.TRANSLATION_KEY},
{key: CONST.FEEDBACK_SURVEY_OPTIONS.BUSINESS_CLOSING.ID, label: CONST.FEEDBACK_SURVEY_OPTIONS.BUSINESS_CLOSING.TRANSLATION_KEY},
];

function FeedbackSurvey({title, description, onSubmit, optionRowStyles}: FeedbackSurveyProps) {
const {translate} = useLocalize();
const styles = useThemeStyles();
const theme = useTheme();

const selectCircleStyles: StyleProp<ViewStyle> = {borderColor: theme.border};
const [reason, setReason] = useState<Option>();
const [shouldShowReasonError, setShouldShowReasonError] = useState(false);

const handleOptionSelect = (option: Option) => {
setShouldShowReasonError(false);
setReason(option);
};

const handleSubmit = () => {
if (!reason) {
setShouldShowReasonError(true);
return;
}

onSubmit(reason);
};

return (
<View style={[styles.flexGrow1, styles.justifyContentBetween]}>
<View style={styles.mh5}>
<Text style={styles.textHeadline}>{title}</Text>
<Text style={[styles.mt1, styles.mb3, styles.textNormalThemeText]}>{description}</Text>
<SingleOptionSelector
options={OPTIONS}
optionRowStyles={[styles.mb7, optionRowStyles]}
selectCircleStyles={selectCircleStyles}
selectedOptionKey={reason?.key}
onSelectOption={handleOptionSelect}
/>
</View>
<FixedFooter>
<FormAlertWithSubmitButton
isAlertVisible={shouldShowReasonError}
onSubmit={handleSubmit}
message="common.error.pleaseCompleteForm"
buttonText={translate('common.submit')}
/>
</FixedFooter>
</View>
);
}

FeedbackSurvey.displayName = 'FeedbackSurvey';

export default FeedbackSurvey;
13 changes: 10 additions & 3 deletions src/components/SingleOptionSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from 'react';
import type {StyleProp, ViewStyle} from 'react-native';
import {View} from 'react-native';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
Expand All @@ -22,9 +23,15 @@ type SingleOptionSelectorProps = {

/** Function to be called when an option is selected */
onSelectOption?: (item: Item) => void;

/** Styles for the option row element */
optionRowStyles?: StyleProp<ViewStyle>;

/** Styles for the select circle */
selectCircleStyles?: StyleProp<ViewStyle>;
};

function SingleOptionSelector({options = [], selectedOptionKey, onSelectOption = () => {}}: SingleOptionSelectorProps) {
function SingleOptionSelector({options = [], selectedOptionKey, onSelectOption = () => {}, optionRowStyles, selectCircleStyles}: SingleOptionSelectorProps) {
const styles = useThemeStyles();
const {translate} = useLocalize();
return (
Expand All @@ -35,7 +42,7 @@ function SingleOptionSelector({options = [], selectedOptionKey, onSelectOption =
key={option.key}
>
<PressableWithoutFeedback
style={styles.singleOptionSelectorRow}
style={[styles.singleOptionSelectorRow, optionRowStyles]}
onPress={() => onSelectOption(option)}
role={CONST.ROLE.BUTTON}
accessibilityState={{checked: selectedOptionKey === option.key}}
Expand All @@ -44,7 +51,7 @@ function SingleOptionSelector({options = [], selectedOptionKey, onSelectOption =
>
<SelectCircle
isChecked={selectedOptionKey ? selectedOptionKey === option.key : false}
selectCircleStyles={[styles.ml0, styles.singleOptionSelectorCircle]}
selectCircleStyles={[styles.ml0, styles.singleOptionSelectorCircle, selectCircleStyles]}
/>
<Text>{translate(option.label)}</Text>
</PressableWithoutFeedback>
Expand Down
19 changes: 19 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ export default {
enterAmount: 'Enter an amount.',
enterDate: 'Enter a date.',
invalidTimeRange: 'Please enter a time using the 12-hour clock format (e.g., 2:30 PM).',
pleaseCompleteForm: 'Please complete the form above to continue.',
},
comma: 'comma',
semicolon: 'semicolon',
Expand Down Expand Up @@ -3284,5 +3285,23 @@ export default {
security: 'Expensify is PCI-DSS compliant, uses bank-level encryption, and utilizes redundant infrastructure to protect your data.',
learnMoreAboutSecurity: 'Learn more about our security.',
},
subscriptionSettings: {
title: 'Subscription settings',
autoRenew: 'Auto-renew',
autoIncrease: 'Auto-increase annual seats',
saveUpTo: ({amountSaved}) => `Save up to $${amountSaved}/month per active member`,
automaticallyIncrease:
'Automatically increase your annual seats to accommodate for active members that exceed your subscription size. Note: This will extend your annual subscription end date.',
disableAutoRenew: 'Disable auto-renew',
helpUsImprove: 'Help us improve Expensify',
whatsMainReason: 'What’s the main reason you’re disabling auto-renew on your subscription?',
renewsOn: ({date}) => `Renews on ${date}.`,
},
},
feedbackSurvey: {
tooLimited: 'Functionality needs improvement',
tooExpensive: 'Too expensive',
inadequateSupport: 'Inadequate customer support',
businessClosing: 'Company closing, downsizing, or acquired',
},
} satisfies TranslationBase;
19 changes: 19 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ export default {
enterAmount: 'Introduce un importe.',
enterDate: 'Introduce una fecha.',
invalidTimeRange: 'Por favor, introduce una hora entre 1 y 12 (por ejemplo, 2:30 PM).',
pleaseCompleteForm: 'Por favor complete el formulario de arriba para continuar..',
},
comma: 'la coma',
semicolon: 'el punto y coma',
Expand Down Expand Up @@ -3791,5 +3792,23 @@ export default {
security: 'Expensify es PCI-DSS obediente, utiliza cifrado a nivel bancario, y emplea infraestructura redundante para proteger tus datos.',
learnMoreAboutSecurity: 'Conozca más sobre nuestra seguridad.',
},
subscriptionSettings: {
title: 'Configuración de suscripción',
autoRenew: 'Auto-renovación',
autoIncrease: 'Auto-incremento',
saveUpTo: ({amountSaved}) => `Ahorre hasta $${amountSaved} al mes por miembro activo`,
automaticallyIncrease:
'Aumenta automáticamente tus plazas anuales para dar lugar a los miembros activos que superen el tamaño de tu suscripción. Nota: Esto ampliará la fecha de finalización de tu suscripción anual.',
disableAutoRenew: 'Desactivar auto-renovación',
helpUsImprove: 'Ayúdanos a mejorar Expensify',
whatsMainReason: '¿Cuál es la razón principal por la que deseas desactivar la auto-renovación de tu suscripción?',
renewsOn: ({date}) => `Se renovará el ${date}.`,
},
},
feedbackSurvey: {
tooLimited: 'Hay que mejorar la funcionalidad',
tooExpensive: 'Demasiado caro',
inadequateSupport: 'Atención al cliente inadecuada',
businessClosing: 'Cierre, reducción, o adquisición de la empresa',
},
} satisfies EnglishTranslation;
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ const SettingsModalStackNavigator = createModalStackNavigator<SettingsNavigatorP
[SCREENS.SETTINGS.PROFILE.STATUS_CLEAR_AFTER_DATE]: () => require('../../../../pages/settings/Profile/CustomStatus/SetDatePage').default as React.ComponentType,
[SCREENS.SETTINGS.PROFILE.STATUS_CLEAR_AFTER_TIME]: () => require('../../../../pages/settings/Profile/CustomStatus/SetTimePage').default as React.ComponentType,
[SCREENS.SETTINGS.SUBSCRIPTION.SIZE]: () => require('../../../../pages/settings/Subscription/SubscriptionSize/SubscriptionSizePage').default as React.ComponentType,
[SCREENS.SETTINGS.SUBSCRIPTION.DISABLE_AUTO_RENEW_SURVEY]: () => require('../../../../pages/settings/Subscription/DisableAutoRenewSurveyPage').default as React.ComponentType,
[SCREENS.WORKSPACE.RATE_AND_UNIT]: () => require('../../../../pages/workspace/reimburse/WorkspaceRateAndUnitPage/InitialPage').default as React.ComponentType,
[SCREENS.WORKSPACE.RATE_AND_UNIT_RATE]: () => require('../../../../pages/workspace/reimburse/WorkspaceRateAndUnitPage/RatePage').default as React.ComponentType,
[SCREENS.WORKSPACE.RATE_AND_UNIT_UNIT]: () => require('../../../../pages/workspace/reimburse/WorkspaceRateAndUnitPage/UnitPage').default as React.ComponentType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const CENTRAL_PANE_TO_RHP_MAPPING: Partial<Record<CentralPaneName, string[]>> =
[SCREENS.SETTINGS.SAVE_THE_WORLD]: [SCREENS.I_KNOW_A_TEACHER, SCREENS.INTRO_SCHOOL_PRINCIPAL, SCREENS.I_AM_A_TEACHER],
[SCREENS.SETTINGS.TROUBLESHOOT]: [SCREENS.SETTINGS.CONSOLE],
[SCREENS.SEARCH.CENTRAL_PANE]: [SCREENS.SEARCH.REPORT_RHP],
[SCREENS.SETTINGS.SUBSCRIPTION.ROOT]: [SCREENS.SETTINGS.SUBSCRIPTION.ADD_PAYMENT_CARD, SCREENS.SETTINGS.SUBSCRIPTION.SIZE],
[SCREENS.SETTINGS.SUBSCRIPTION.ROOT]: [SCREENS.SETTINGS.SUBSCRIPTION.ADD_PAYMENT_CARD, SCREENS.SETTINGS.SUBSCRIPTION.SIZE, SCREENS.SETTINGS.SUBSCRIPTION.DISABLE_AUTO_RENEW_SURVEY],
};

export default CENTRAL_PANE_TO_RHP_MAPPING;
3 changes: 3 additions & 0 deletions src/libs/Navigation/linkingConfig/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,9 @@ const config: LinkingOptions<RootStackParamList>['config'] = {
[SCREENS.SETTINGS.SUBSCRIPTION.SIZE]: {
path: ROUTES.SETTINGS_SUBSCRIPTION_SIZE,
},
[SCREENS.SETTINGS.SUBSCRIPTION.DISABLE_AUTO_RENEW_SURVEY]: {
path: ROUTES.SETTINGS_SUBSCRIPTION_DISABLE_AUTO_RENEW_SURVEY,
},
[SCREENS.WORKSPACE.CURRENCY]: {
path: ROUTES.WORKSPACE_PROFILE_CURRENCY.route,
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React from 'react';
import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView';
import ScreenWrapper from '@components/ScreenWrapper';

function DisableAutoRenewSurveyPage() {
return (
<ScreenWrapper
testID={DisableAutoRenewSurveyPage.displayName}
includeSafeAreaPaddingBottom
shouldEnableMaxHeight
>
<FullPageNotFoundView shouldShow />
</ScreenWrapper>
);
}

DisableAutoRenewSurveyPage.displayName = 'DisableAutoRenewSurveyPage';

export default DisableAutoRenewSurveyPage;
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React from 'react';
import FeedbackSurvey from '@components/FeedbackSurvey';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import ScreenWrapper from '@components/ScreenWrapper';
import ScrollView from '@components/ScrollView';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@libs/Navigation/Navigation';

function DisableAutoRenewSurveyPage() {
const {translate} = useLocalize();
const styles = useThemeStyles();

const handleSubmit = () => {
// TODO API call to submit feedback will be implemented in next phase
};

return (
<ScreenWrapper
testID={DisableAutoRenewSurveyPage.displayName}
includeSafeAreaPaddingBottom={false}
shouldEnablePickerAvoiding={false}
shouldEnableMaxHeight
>
<HeaderWithBackButton
title={translate('subscription.subscriptionSettings.disableAutoRenew')}
onBackButtonPress={Navigation.goBack}
/>
<ScrollView contentContainerStyle={[styles.flexGrow1, styles.pt3]}>
<FeedbackSurvey
title={translate('subscription.subscriptionSettings.helpUsImprove')}
description={translate('subscription.subscriptionSettings.whatsMainReason')}
onSubmit={handleSubmit}
optionRowStyles={styles.flex1}
/>
</ScrollView>
</ScreenWrapper>
);
}

DisableAutoRenewSurveyPage.displayName = 'DisableAutoRenewSurveyPage';

export default DisableAutoRenewSurveyPage;
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
function SubscriptionSettings() {
return null;
}

export default SubscriptionSettings;
Loading
Loading