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

Change design of WorkspaceMoreFeaturePage sections. #55556

Merged
133 changes: 85 additions & 48 deletions src/pages/workspace/WorkspaceMoreFeaturesPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,31 @@ import useNetwork from '@hooks/useNetwork';
import usePermissions from '@hooks/usePermissions';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useStyledSafeAreaInsets from '@hooks/useStyledSafeAreaInsets';
import useStyleUtils from '@hooks/useStyleUtils';
import useThemeStyles from '@hooks/useThemeStyles';
import * as CardUtils from '@libs/CardUtils';
import * as ErrorUtils from '@libs/ErrorUtils';
import {getCompanyFeeds} from '@libs/CardUtils';
import {getLatestErrorField} from '@libs/ErrorUtils';
import Navigation from '@libs/Navigation/Navigation';
import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types';
import type {FullScreenNavigatorParamList} from '@libs/Navigation/types';
import {getPerDiemCustomUnit, isControlPolicy} from '@libs/PolicyUtils';
import * as Category from '@userActions/Policy/Category';
import * as DistanceRate from '@userActions/Policy/DistanceRate';
import * as PerDiem from '@userActions/Policy/PerDiem';
import * as Policy from '@userActions/Policy/Policy';
import * as Tag from '@userActions/Policy/Tag';
import * as Report from '@userActions/Report';
import {enablePolicyCategories} from '@userActions/Policy/Category';
import {enablePolicyDistanceRates} from '@userActions/Policy/DistanceRate';
import {enablePerDiem} from '@userActions/Policy/PerDiem';
import {
clearPolicyErrorField,
enableCompanyCards,
enableExpensifyCard,
enablePolicyConnections,
enablePolicyInvoicing,
enablePolicyReportFields,
enablePolicyRules,
enablePolicyTaxes,
enablePolicyWorkflows,
openPolicyMoreFeaturesPage,
} from '@userActions/Policy/Policy';
import {enablePolicyTags} from '@userActions/Policy/Tag';
import {navigateToConciergeChat} from '@userActions/Report';
import CONST from '@src/CONST';
import type {TranslationPaths} from '@src/languages/types';
import ONYXKEYS from '@src/ONYXKEYS';
Expand Down Expand Up @@ -63,6 +75,7 @@ type SectionObject = {

function WorkspaceMoreFeaturesPage({policy, route}: WorkspaceMoreFeaturesPageProps) {
const styles = useThemeStyles();
const stylesutils = useStyleUtils();
const {shouldUseNarrowLayout} = useResponsiveLayout();
const {safeAreaPaddingBottomStyle} = useStyledSafeAreaInsets();
const {translate} = useLocalize();
Expand Down Expand Up @@ -103,7 +116,7 @@ function WorkspaceMoreFeaturesPage({policy, route}: WorkspaceMoreFeaturesPagePro
if (!policyID) {
return;
}
DistanceRate.enablePolicyDistanceRates(policyID, isEnabled);
enablePolicyDistanceRates(policyID, isEnabled);
},
},
{
Expand All @@ -117,7 +130,7 @@ function WorkspaceMoreFeaturesPage({policy, route}: WorkspaceMoreFeaturesPagePro
if (!policyID) {
return;
}
Policy.enableExpensifyCard(policyID, isEnabled);
enableExpensifyCard(policyID, isEnabled);
},
disabledAction: () => {
setIsDisableExpensifyCardWarningModalOpen(true);
Expand All @@ -131,7 +144,7 @@ function WorkspaceMoreFeaturesPage({policy, route}: WorkspaceMoreFeaturesPagePro
subtitleTranslationKey: 'workspace.moreFeatures.companyCards.subtitle',
isActive: policy?.areCompanyCardsEnabled ?? false,
pendingAction: policy?.pendingFields?.areCompanyCardsEnabled,
disabled: !isEmptyObject(CardUtils.getCompanyFeeds(cardFeeds)),
disabled: !isEmptyObject(getCompanyFeeds(cardFeeds)),
action: (isEnabled: boolean) => {
if (!policyID) {
return;
Expand All @@ -140,7 +153,7 @@ function WorkspaceMoreFeaturesPage({policy, route}: WorkspaceMoreFeaturesPagePro
Navigation.navigate(ROUTES.WORKSPACE_UPGRADE.getRoute(policyID, CONST.UPGRADE_FEATURE_INTRO_MAPPING.companyCards.alias, ROUTES.WORKSPACE_MORE_FEATURES.getRoute(policyID)));
return;
}
Policy.enableCompanyCards(policyID, isEnabled);
enableCompanyCards(policyID, isEnabled);
},
disabledAction: () => {
setIsDisableCompanyCardsWarningModalOpen(true);
Expand All @@ -162,7 +175,7 @@ function WorkspaceMoreFeaturesPage({policy, route}: WorkspaceMoreFeaturesPagePro
Navigation.navigate(ROUTES.WORKSPACE_UPGRADE.getRoute(policyID, CONST.UPGRADE_FEATURE_INTRO_MAPPING.perDiem.alias, ROUTES.WORKSPACE_MORE_FEATURES.getRoute(policyID)));
return;
}
PerDiem.enablePerDiem(policyID, isEnabled, perDiemCustomUnit?.customUnitID);
enablePerDiem(policyID, isEnabled, perDiemCustomUnit?.customUnitID);
},
});
}
Expand All @@ -178,7 +191,7 @@ function WorkspaceMoreFeaturesPage({policy, route}: WorkspaceMoreFeaturesPagePro
if (!policyID) {
return;
}
Policy.enablePolicyWorkflows(policyID, isEnabled);
enablePolicyWorkflows(policyID, isEnabled);
},
},
{
Expand All @@ -196,7 +209,7 @@ function WorkspaceMoreFeaturesPage({policy, route}: WorkspaceMoreFeaturesPagePro
Navigation.navigate(ROUTES.WORKSPACE_UPGRADE.getRoute(policyID, CONST.UPGRADE_FEATURE_INTRO_MAPPING.rules.alias, ROUTES.WORKSPACE_MORE_FEATURES.getRoute(policyID)));
return;
}
Policy.enablePolicyRules(policyID, isEnabled);
enablePolicyRules(policyID, isEnabled);
},
},
];
Expand All @@ -212,7 +225,7 @@ function WorkspaceMoreFeaturesPage({policy, route}: WorkspaceMoreFeaturesPagePro
if (!policyID) {
return;
}
Policy.enablePolicyInvoicing(policyID, isEnabled);
enablePolicyInvoicing(policyID, isEnabled);
},
},
];
Expand All @@ -230,7 +243,7 @@ function WorkspaceMoreFeaturesPage({policy, route}: WorkspaceMoreFeaturesPagePro
if (!policyID) {
return;
}
Category.enablePolicyCategories(policyID, isEnabled);
enablePolicyCategories(policyID, isEnabled);
},
},
{
Expand All @@ -245,7 +258,7 @@ function WorkspaceMoreFeaturesPage({policy, route}: WorkspaceMoreFeaturesPagePro
if (!policyID) {
return;
}
Tag.enablePolicyTags(policyID, isEnabled);
enablePolicyTags(policyID, isEnabled);
},
},
{
Expand All @@ -260,7 +273,7 @@ function WorkspaceMoreFeaturesPage({policy, route}: WorkspaceMoreFeaturesPagePro
if (!policyID) {
return;
}
Policy.enablePolicyTaxes(policyID, isEnabled);
enablePolicyTaxes(policyID, isEnabled);
},
},
{
Expand All @@ -283,7 +296,7 @@ function WorkspaceMoreFeaturesPage({policy, route}: WorkspaceMoreFeaturesPagePro
return;
}

Policy.enablePolicyReportFields(policyID, true);
enablePolicyReportFields(policyID, true);
return;
}
setIsReportFieldsWarningModalOpen(true);
Expand All @@ -308,15 +321,15 @@ function WorkspaceMoreFeaturesPage({policy, route}: WorkspaceMoreFeaturesPagePro
if (!policyID) {
return;
}
Policy.enablePolicyConnections(policyID, isEnabled);
enablePolicyConnections(policyID, isEnabled);
},
disabled: hasAccountingConnection,
errors: ErrorUtils.getLatestErrorField(policy ?? {}, CONST.POLICY.MORE_FEATURES.ARE_CONNECTIONS_ENABLED),
errors: getLatestErrorField(policy ?? {}, CONST.POLICY.MORE_FEATURES.ARE_CONNECTIONS_ENABLED),
onCloseError: () => {
if (!policyID) {
return;
}
Policy.clearPolicyErrorField(policyID, CONST.POLICY.MORE_FEATURES.ARE_CONNECTIONS_ENABLED);
clearPolicyErrorField(policyID, CONST.POLICY.MORE_FEATURES.ARE_CONNECTIONS_ENABLED);
},
},
];
Expand All @@ -327,6 +340,16 @@ function WorkspaceMoreFeaturesPage({policy, route}: WorkspaceMoreFeaturesPagePro
subtitleTranslationKey: 'workspace.moreFeatures.spendSection.subtitle',
items: spendItems,
},
{
titleTranslationKey: 'workspace.moreFeatures.integrateSection.title',
subtitleTranslationKey: 'workspace.moreFeatures.integrateSection.subtitle',
items: integrateItems,
},
{
titleTranslationKey: 'workspace.moreFeatures.organizeSection.title',
subtitleTranslationKey: 'workspace.moreFeatures.organizeSection.subtitle',
items: organizeItems,
},
{
titleTranslationKey: 'workspace.moreFeatures.manageSection.title',
subtitleTranslationKey: 'workspace.moreFeatures.manageSection.subtitle',
Expand All @@ -337,23 +360,13 @@ function WorkspaceMoreFeaturesPage({policy, route}: WorkspaceMoreFeaturesPagePro
subtitleTranslationKey: 'workspace.moreFeatures.earnSection.subtitle',
items: earnItems,
},
{
titleTranslationKey: 'workspace.moreFeatures.organizeSection.title',
subtitleTranslationKey: 'workspace.moreFeatures.organizeSection.subtitle',
items: organizeItems,
},
{
titleTranslationKey: 'workspace.moreFeatures.integrateSection.title',
subtitleTranslationKey: 'workspace.moreFeatures.integrateSection.subtitle',
items: integrateItems,
},
];

const renderItem = useCallback(
(item: Item) => (
<View
key={item.titleTranslationKey}
style={styles.mt7}
style={[styles.workspaceSectionMoreFeaturesItem, shouldUseNarrowLayout && styles.flexBasis100, shouldUseNarrowLayout && stylesutils.getMinimumWidth(0)]}
>
<ToggleSettingOptionRow
icon={item.icon}
Expand All @@ -372,31 +385,57 @@ function WorkspaceMoreFeaturesPage({policy, route}: WorkspaceMoreFeaturesPagePro
/>
</View>
),
[styles, translate],
[styles, stylesutils, shouldUseNarrowLayout, translate],
);

/** Used to fill row space in the Section items when there are odd number of items to create equal margins for last odd item. */
const sectionRowFillerItem = useCallback(
(section: SectionObject) => {
if (section.items.length % 2 === 0) {
return null;
}

return (
<View
key="section-filler-col"
aria-hidden
accessibilityElementsHidden
style={[
styles.workspaceSectionMoreFeaturesItem,
shouldUseNarrowLayout && stylesutils.getMinimumWidth(0),
styles.p0,
styles.mt0,
styles.visibilityHidden,
styles.bgTransparent,
]}
/>
);
},
[styles, stylesutils, shouldUseNarrowLayout],
);

const renderSection = useCallback(
(section: SectionObject) => (
<View
key={section.titleTranslationKey}
style={[styles.mt3, shouldUseNarrowLayout ? styles.workspaceSectionMobile : styles.workspaceSection]}
style={[styles.mt3, shouldUseNarrowLayout ? styles.workspaceSectionMobile : {}]}
>
<Section
containerStyles={shouldUseNarrowLayout ? styles.p5 : styles.p8}
title={translate(section.titleTranslationKey)}
titleStyles={styles.textStrong}
subtitle={translate(section.subtitleTranslationKey)}
containerStyles={[styles.ph1, styles.pv0, styles.bgTransparent, styles.noBorderRadius]}
childrenStyles={[styles.flexRow, styles.flexWrap, styles.columnGap3]}
renderTitle={() => <Text style={styles.mutedNormalTextLabel}>{translate(section.titleTranslationKey)}</Text>}
subtitleMuted
>
{section.items.map(renderItem)}
{sectionRowFillerItem(section)}
</Section>
</View>
),
[shouldUseNarrowLayout, styles, renderItem, translate],
[shouldUseNarrowLayout, styles, renderItem, translate, sectionRowFillerItem],
);

const fetchFeatures = useCallback(() => {
Policy.openPolicyMoreFeaturesPage(route.params.policyID);
openPolicyMoreFeaturesPage(route.params.policyID);
}, [route.params.policyID]);

useNetwork({onReconnect: fetchFeatures});
Expand Down Expand Up @@ -426,9 +465,7 @@ function WorkspaceMoreFeaturesPage({policy, route}: WorkspaceMoreFeaturesPagePro
/>

<ScrollView contentContainerStyle={safeAreaPaddingBottomStyle}>
<Text style={[styles.ph5, styles.mb4, styles.mt3, styles.textSupporting, shouldUseNarrowLayout ? styles.workspaceSectionMobile : styles.workspaceSection]}>
{translate('workspace.moreFeatures.subtitle')}
</Text>
<Text style={[styles.ph5, styles.mb5, styles.mt3, styles.textSupporting, styles.workspaceSectionMobile]}>{translate('workspace.moreFeatures.subtitle')}</Text>
{sections.map(renderSection)}
</ScrollView>

Expand Down Expand Up @@ -470,7 +507,7 @@ function WorkspaceMoreFeaturesPage({policy, route}: WorkspaceMoreFeaturesPagePro
return;
}
setIsReportFieldsWarningModalOpen(false);
Policy.enablePolicyReportFields(policyID, false);
enablePolicyReportFields(policyID, false);
}}
onCancel={() => setIsReportFieldsWarningModalOpen(false)}
prompt={translate('workspace.reportFields.disableReportFieldsConfirmation')}
Expand All @@ -483,7 +520,7 @@ function WorkspaceMoreFeaturesPage({policy, route}: WorkspaceMoreFeaturesPagePro
isVisible={isDisableExpensifyCardWarningModalOpen}
onConfirm={() => {
setIsDisableExpensifyCardWarningModalOpen(false);
Report.navigateToConciergeChat(true);
navigateToConciergeChat(true);
}}
onCancel={() => setIsDisableExpensifyCardWarningModalOpen(false)}
prompt={translate('workspace.moreFeatures.expensifyCard.disableCardPrompt')}
Expand All @@ -495,7 +532,7 @@ function WorkspaceMoreFeaturesPage({policy, route}: WorkspaceMoreFeaturesPagePro
isVisible={isDisableCompanyCardsWarningModalOpen}
onConfirm={() => {
setIsDisableCompanyCardsWarningModalOpen(false);
Report.navigateToConciergeChat(true);
navigateToConciergeChat(true);
}}
onCancel={() => setIsDisableCompanyCardsWarningModalOpen(false)}
prompt={translate('workspace.moreFeatures.companyCards.disableCardPrompt')}
Expand Down
13 changes: 13 additions & 0 deletions src/styles/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4918,6 +4918,19 @@ const styles = (theme: ThemeColors) =>
alignSelf: 'center',
},

workspaceSectionMoreFeaturesItem: {
backgroundColor: theme.cardBG,
borderRadius: variables.componentBorderRadiusNormal,
paddingHorizontal: 16,
paddingVertical: 20,
minWidth: 350,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kept a 350 width minimum width till it be a column layout.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have any breakpoints we already use in the product? Like the same way we would use a media query... if we are at a "tablet" size, maybe that's when we switch to a single column.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just throwing it out there in case we can kind of align on how the app responds to different widths.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to force 1 column on tablet portraits where the screen width is less than 800px?

Currently, 350 will allow it to be fit two rows in 760 < screenwidth >= 800.

image

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you show me what 1 column looks like at 800px screen width? Just to compare. Thanks!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Enabled 1-column on < 800 Screens.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@parasharrajat, I have one doubt here. Passing minWidth: 350 might cause some UI distortion on devices with smaller widths. While testing using Chrome's Inspect tool on the Fold 5 (a virtual device in Chrome Inspect, not an actual one), I noticed a UI issue where the card extends beyond the screen since the width for that device is 344px.
Screenshot 2025-01-22 at 6 50 50 PM

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me fix that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jayeshmangwani Done. Check now.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checking

flexGrow: 1,
flexShrink: 1,
// Choosing a lowest value just above the threshold for the items to adjust width against the various screens. Only 2 items are shown 35 * 2 = 70 thus third item of 35% width can't fit forcing a two column layout.
flexBasis: '35%',
marginTop: 12,
},

aspectRatioLottie: (animation: DotLottieAnimation) => ({aspectRatio: animation.w / animation.h}),

receiptDropHeaderGap: {
Expand Down
4 changes: 4 additions & 0 deletions src/styles/utils/flex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,10 @@ export default {
flexBasis: 'auto',
},

flexBasis100: {
flexBasis: '100%',
},

flexBasis0: {
flexBasis: 0,
},
Expand Down
4 changes: 4 additions & 0 deletions src/styles/utils/spacing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -717,6 +717,10 @@ export default {
rowGap: 16,
},

columnGap3: {
columnGap: 12,
},

minHeight5: {
minHeight: 20,
},
Expand Down
Loading