Skip to content

Commit

Permalink
Merge pull request Expensify#43855 from callstack-internal/pac-guerre…
Browse files Browse the repository at this point in the history
…iro/feature/restricted-action-screen

[Free trial] Implement Restricted Action screen
  • Loading branch information
chiragsalian authored Jun 24, 2024
2 parents 7279cbc + b27da18 commit 3ef375f
Show file tree
Hide file tree
Showing 17 changed files with 378 additions and 0 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -917,6 +917,10 @@ const ROUTES = {
route: 'settings/workspaces/:policyID/accounting/quickbooks-online/import/taxes',
getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/quickbooks-online/import/taxes` as const,
},
RESTRICTED_ACTION: {
route: 'restricted-action/workspace/:policyID',
getRoute: (policyID: string) => `restricted-action/workspace/${policyID}` as const,
},
} as const;

/**
Expand Down
2 changes: 2 additions & 0 deletions src/SCREENS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ const SCREENS = {
TRAVEL: 'Travel',
SEARCH_REPORT: 'SearchReport',
SETTINGS_CATEGORIES: 'SettingsCategories',
RESTRICTED_ACTION: 'RestrictedAction',
},
ONBOARDING_MODAL: {
ONBOARDING: 'Onboarding',
Expand Down Expand Up @@ -384,6 +385,7 @@ const SCREENS = {
KEYBOARD_SHORTCUTS: 'KeyboardShortcuts',
TRANSACTION_RECEIPT: 'TransactionReceipt',
FEATURE_TRAINING_ROOT: 'FeatureTraining_Root',
RESTRICTED_ACTION_ROOT: 'RestrictedAction_Root',
} as const;

type Screen = DeepValueOf<typeof SCREENS>;
Expand Down
2 changes: 2 additions & 0 deletions src/components/Icon/Illustrations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ import House from '@assets/images/simple-illustrations/simple-illustration__hous
import InvoiceBlue from '@assets/images/simple-illustrations/simple-illustration__invoice.svg';
import Lightbulb from '@assets/images/simple-illustrations/simple-illustration__lightbulb.svg';
import LockClosed from '@assets/images/simple-illustrations/simple-illustration__lockclosed.svg';
import LockClosedOrange from '@assets/images/simple-illustrations/simple-illustration__lockclosed_orange.svg';
import LockOpen from '@assets/images/simple-illustrations/simple-illustration__lockopen.svg';
import Luggage from '@assets/images/simple-illustrations/simple-illustration__luggage.svg';
import Mailbox from '@assets/images/simple-illustrations/simple-illustration__mailbox.svg';
Expand Down Expand Up @@ -192,4 +193,5 @@ export {
SendMoney,
CheckmarkCircle,
CreditCardEyes,
LockClosedOrange,
};
13 changes: 13 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2730,6 +2730,19 @@ export default {
errorDescriptionPartTwo: 'reach out to Concierge',
errorDescriptionPartThree: 'for help.',
},
restrictedAction: {
restricted: 'Restricted',
actionsAreCurrentlyRestricted: ({workspaceName}) => `Actions on the ${workspaceName} workspace are currently restricted`,
workspaceOwnerWillNeedToAddOrUpdatePaymentCard: ({workspaceOwnerName}) =>
`Workspace owner, ${workspaceOwnerName} will need to add or update the payment card on file to unlock new workspace activity.`,
youWillNeedToAddOrUpdatePaymentCard: "You'll need to add or update the payment card on file to unlock new workspace activity.",
addPaymentCardToUnlock: 'Add a payment card to unlock!',
addPaymentCardToContinueUsingWorkspace: 'Add a payment card to continue using this workspace',
pleaseReachOutToYourWorkspaceAdmin: 'Please reach out to your workspace admin for any questions.',
chatWithYourAdmin: 'Chat with your admin',
chatInAdmins: 'Chat in #admins',
addPaymentCard: 'Add payment card',
},
},
getAssistancePage: {
title: 'Get assistance',
Expand Down
13 changes: 13 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2768,6 +2768,19 @@ export default {
errorDescriptionPartTwo: 'contacta con el conserje',
errorDescriptionPartThree: 'por ayuda.',
},
restrictedAction: {
restricted: 'Restringido',
actionsAreCurrentlyRestricted: ({workspaceName}) => `Las acciones en el espacio de trabajo ${workspaceName} están actualmente restringidas`,
workspaceOwnerWillNeedToAddOrUpdatePaymentCard: ({workspaceOwnerName}) =>
`El propietario del espacio de trabajo, ${workspaceOwnerName} tendrá que añadir o actualizar la tarjeta de pago registrada para desbloquear nueva actividad en el espacio de trabajo.`,
youWillNeedToAddOrUpdatePaymentCard: 'Debes añadir o actualizar la tarjeta de pago registrada para desbloquear nueva actividad en el espacio de trabajo.',
addPaymentCardToUnlock: 'Añade una tarjeta para desbloquearlo!',
addPaymentCardToContinueUsingWorkspace: 'Añade una tarjeta de pago para seguir utilizando este espacio de trabajo',
pleaseReachOutToYourWorkspaceAdmin: 'Si tienes alguna pregunta, ponte en contacto con el administrador de su espacio de trabajo.',
chatWithYourAdmin: 'Chatea con tu administrador',
chatInAdmins: 'Chatea en #admins',
addPaymentCard: 'Agregar tarjeta de pago',
},
},
getAssistancePage: {
title: 'Obtener ayuda',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,10 @@ const SearchReportModalStackNavigator = createModalStackNavigator<SearchReportPa
[SCREENS.SEARCH.REPORT_RHP]: () => require('../../../../pages/home/ReportScreen').default as React.ComponentType,
});

const RestrictedActionModalStackNavigator = createModalStackNavigator<SearchReportParamList>({
[SCREENS.RESTRICTED_ACTION_ROOT]: () => require('../../../../pages/RestrictedAction/Workspace/WorkspaceRestrictedActionPage').default as React.ComponentType,
});

export {
AddPersonalBankAccountModalStackNavigator,
EditRequestStackNavigator,
Expand Down Expand Up @@ -406,4 +410,5 @@ export {
WalletStatementStackNavigator,
TransactionDuplicateStackNavigator,
SearchReportModalStackNavigator,
RestrictedActionModalStackNavigator,
};
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,10 @@ function RightModalNavigator({navigation}: RightModalNavigatorProps) {
name={SCREENS.RIGHT_MODAL.SEARCH_REPORT}
component={ModalStackNavigators.SearchReportModalStackNavigator}
/>
<Stack.Screen
name={SCREENS.RIGHT_MODAL.RESTRICTED_ACTION}
component={ModalStackNavigators.RestrictedActionModalStackNavigator}
/>
</Stack.Navigator>
</View>
</NoDropZone>
Expand Down
5 changes: 5 additions & 0 deletions src/libs/Navigation/linkingConfig/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -748,6 +748,11 @@ const config: LinkingOptions<RootStackParamList>['config'] = {
[SCREENS.SEARCH.REPORT_RHP]: ROUTES.SEARCH_REPORT.route,
},
},
[SCREENS.RIGHT_MODAL.RESTRICTED_ACTION]: {
screens: {
[SCREENS.RESTRICTED_ACTION_ROOT]: ROUTES.RESTRICTED_ACTION.route,
},
},
},
},

Expand Down
8 changes: 8 additions & 0 deletions src/libs/Navigation/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -777,6 +777,7 @@ type RightModalNavigatorParamList = {
[SCREENS.RIGHT_MODAL.TRANSACTION_DUPLICATE]: NavigatorScreenParams<TransactionDuplicateNavigatorParamList>;
[SCREENS.RIGHT_MODAL.TRAVEL]: NavigatorScreenParams<TravelNavigatorParamList>;
[SCREENS.RIGHT_MODAL.SEARCH_REPORT]: NavigatorScreenParams<SearchReportParamList>;
[SCREENS.RIGHT_MODAL.RESTRICTED_ACTION]: NavigatorScreenParams<RestrictedActionParamList>;
};

type TravelNavigatorParamList = {
Expand Down Expand Up @@ -953,6 +954,12 @@ type SearchReportParamList = {
};
};

type RestrictedActionParamList = {
[SCREENS.RESTRICTED_ACTION_ROOT]: {
policyID: string;
};
};

type RootStackParamList = PublicScreensParamList & AuthScreensParamList & LeftModalNavigatorParamList;

type BottomTabName = keyof BottomTabNavigatorParamList;
Expand Down Expand Up @@ -1018,4 +1025,5 @@ export type {
WelcomeVideoModalNavigatorParamList,
TransactionDuplicateNavigatorParamList,
SearchReportParamList,
RestrictedActionParamList,
};
7 changes: 7 additions & 0 deletions src/libs/PolicyUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,12 @@ function isExpensifyTeam(email: string | undefined): boolean {
const isPolicyAdmin = (policy: OnyxInputOrEntry<Policy> | EmptyObject, currentUserLogin?: string): boolean =>
(policy?.role ?? (currentUserLogin && policy?.employeeList?.[currentUserLogin]?.role)) === CONST.POLICY.ROLE.ADMIN;

/**
* Checks if the current user is an user of the policy.
*/
const isPolicyUser = (policy: OnyxInputOrEntry<Policy> | EmptyObject, currentUserLogin?: string): boolean =>
(policy?.role ?? (currentUserLogin && policy?.employeeList?.[currentUserLogin]?.role)) === CONST.POLICY.ROLE.USER;

/**
* Checks if the policy is a free group policy.
*/
Expand Down Expand Up @@ -533,6 +539,7 @@ export {
isPaidGroupPolicy,
isPendingDeletePolicy,
isPolicyAdmin,
isPolicyUser,
isPolicyEmployee,
isPolicyFeatureEnabled,
isPolicyOwner,
Expand Down
5 changes: 5 additions & 0 deletions src/libs/ReportUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7018,6 +7018,10 @@ function getChatUsedForOnboarding(): OnyxEntry<Report> {
return Object.values(allReports ?? {}).find(isChatUsedForOnboarding);
}

function findPolicyExpenseChatByPolicyID(policyID: string): OnyxEntry<Report> {
return Object.values(allReports ?? {}).find((report) => isPolicyExpenseChat(report) && report?.policyID === policyID);
}

export {
addDomainToShortMention,
areAllRequestsBeingSmartScanned,
Expand Down Expand Up @@ -7292,6 +7296,7 @@ export {
createDraftWorkspaceAndNavigateToConfirmationScreen,
isChatUsedForOnboarding,
getChatUsedForOnboarding,
findPolicyExpenseChatByPolicyID,
};

export type {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import React, {useCallback} from 'react';
import {View} from 'react-native';
import {useOnyx} from 'react-native-onyx';
import Button from '@components/Button';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import * as Illustrations from '@components/Icon/Illustrations';
import ImageSVG from '@components/ImageSVG';
import ScreenWrapper from '@components/ScreenWrapper';
import ScrollView from '@components/ScrollView';
import Text from '@components/Text';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import * as Report from '@libs/actions/Report';
import Navigation from '@libs/Navigation/Navigation';
import * as PolicyUtils from '@libs/PolicyUtils';
import variables from '@styles/variables';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';

type WorkspaceAdminRestrictedActionProps = {
policyID: string;
};

function WorkspaceAdminRestrictedAction({policyID}: WorkspaceAdminRestrictedActionProps) {
const {translate} = useLocalize();
const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`);
const styles = useThemeStyles();

const openAdminsReport = useCallback(() => {
const reportID = `${PolicyUtils.getPolicy(policyID)?.chatReportIDAdmins}` ?? '-1';
Report.openReport(reportID);
Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(reportID));
}, [policyID]);

return (
<ScreenWrapper
includeSafeAreaPaddingBottom
testID={WorkspaceAdminRestrictedAction.displayName}
>
<HeaderWithBackButton
title={translate('workspace.restrictedAction.restricted')}
onBackButtonPress={Navigation.goBack}
/>
<ScrollView
style={[styles.p5, styles.pt0]}
contentContainerStyle={styles.flexGrow1}
>
<View style={[styles.flex1, styles.alignItemsCenter, styles.justifyContentCenter, styles.mb15]}>
<ImageSVG
src={Illustrations.LockClosedOrange}
width={variables.restrictedActionIllustrationHeight}
height={variables.restrictedActionIllustrationHeight}
/>
<Text style={[styles.textHeadlineH1, styles.textAlignCenter]}>
{translate('workspace.restrictedAction.actionsAreCurrentlyRestricted', {workspaceName: policy?.name})}
</Text>
<Text style={[styles.textLabelSupportingEmptyValue, styles.textAlignCenter, styles.lh20, styles.mt2]}>
{translate('workspace.restrictedAction.workspaceOwnerWillNeedToAddOrUpdatePaymentCard', {workspaceOwnerName: policy?.owner})}
</Text>
</View>
<Button
text={translate('workspace.restrictedAction.chatInAdmins')}
onPress={openAdminsReport}
success
large
/>
</ScrollView>
</ScreenWrapper>
);
}

WorkspaceAdminRestrictedAction.displayName = 'WorkspaceAdminRestrictedAction';

export default WorkspaceAdminRestrictedAction;
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import React from 'react';
import {View} from 'react-native';
import Badge from '@components/Badge';
import Button from '@components/Button';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import Icon from '@components/Icon';
import * as Expensicons from '@components/Icon/Expensicons';
import * as Illustrations from '@components/Icon/Illustrations';
import ScreenWrapper from '@components/ScreenWrapper';
import ScrollView from '@components/ScrollView';
import Text from '@components/Text';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@libs/Navigation/Navigation';
import variables from '@styles/variables';
import ROUTES from '@src/ROUTES';

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

const addPaymentCard = () => {
Navigation.navigate(ROUTES.SETTINGS_SUBSCRIPTION);
};

return (
<ScreenWrapper
includeSafeAreaPaddingBottom
testID={WorkspaceOwnerRestrictedAction.displayName}
>
<HeaderWithBackButton
title={translate('workspace.restrictedAction.restricted')}
onBackButtonPress={Navigation.goBack}
/>
<ScrollView contentContainerStyle={[styles.ph5, styles.pt3]}>
<View style={[styles.cardSectionContainer, styles.p5, styles.mb0, styles.mh0]}>
<View style={[styles.flexRow, styles.justifyContentBetween, styles.alignItemsStart, styles.mb3]}>
<Icon
src={Illustrations.LockClosedOrange}
height={variables.iconHeader}
width={variables.iconHeader}
/>
<Badge
icon={Expensicons.Unlock}
success
text={translate('workspace.restrictedAction.addPaymentCardToUnlock')}
badgeStyles={styles.alignSelfStart}
/>
</View>
<Text style={[styles.textHeadlineH1, styles.mb4]}>{translate('workspace.restrictedAction.addPaymentCardToContinueUsingWorkspace')}</Text>
<Text style={[styles.textLabelSupportingEmptyValue, styles.mb5]}>{translate('workspace.restrictedAction.youWillNeedToAddOrUpdatePaymentCard')}</Text>
<Button
text={translate('workspace.restrictedAction.addPaymentCard')}
onPress={addPaymentCard}
success
large
/>
</View>
</ScrollView>
</ScreenWrapper>
);
}

WorkspaceOwnerRestrictedAction.displayName = 'WorkspaceOwnerRestrictedAction';

export default WorkspaceOwnerRestrictedAction;
Loading

0 comments on commit 3ef375f

Please sign in to comment.