Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion src/libs/OptionsListUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -827,7 +827,7 @@ function getSearchValueForPhoneOrEmail(searchTerm: string) {
* Verifies that there is at least one enabled option
*/
function hasEnabledOptions(options: PolicyCategories | PolicyTag[]): boolean {
return Object.values(options).some((option) => option.enabled);
return Object.values(options).some((option) => option.enabled && option.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE);
}

/**
Expand Down
109 changes: 90 additions & 19 deletions src/libs/actions/Policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ import getIsNarrowLayout from '@libs/getIsNarrowLayout';
import Log from '@libs/Log';
import Navigation from '@libs/Navigation/Navigation';
import * as NumberUtils from '@libs/NumberUtils';
import * as OptionsListUtils from '@libs/OptionsListUtils';
import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils';
import * as PhoneNumber from '@libs/PhoneNumber';
import * as PolicyUtils from '@libs/PolicyUtils';
Expand Down Expand Up @@ -2872,27 +2873,29 @@ function createWorkspaceFromIOUPayment(iouReport: Report | EmptyObject): string

function setWorkspaceCategoryEnabled(policyID: string, categoriesToUpdate: Record<string, {name: string; enabled: boolean}>) {
const policyCategories = allPolicyCategories?.[`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`] ?? {};
const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`];
const optimisticPolicyCategoriesData = {
...Object.keys(categoriesToUpdate).reduce<PolicyCategories>((acc, key) => {
acc[key] = {
...policyCategories[key],
...categoriesToUpdate[key],
errors: null,
pendingFields: {
enabled: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE,
},
pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE,
};

return acc;
}, {}),
};
const shouldDisableRequiresCategory = !OptionsListUtils.hasEnabledOptions({...policyCategories, ...optimisticPolicyCategoriesData});
const onyxData: OnyxData = {
optimisticData: [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`,
value: {
...Object.keys(categoriesToUpdate).reduce<PolicyCategories>((acc, key) => {
acc[key] = {
...policyCategories[key],
...categoriesToUpdate[key],
errors: null,
pendingFields: {
enabled: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE,
},
pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE,
};

return acc;
}, {}),
},
value: optimisticPolicyCategoriesData,
},
],
successData: [
Expand Down Expand Up @@ -2938,6 +2941,37 @@ function setWorkspaceCategoryEnabled(policyID: string, categoriesToUpdate: Recor
},
],
};
if (shouldDisableRequiresCategory) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

can we do the same for deleteWorkspaceCategories?

function deleteWorkspaceCategories(policyID: string, categoryNamesToDelete: string[]) {

Screen.Recording.2024-04-02.at.9.55.11.at.night.mov

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I updated to do the same when deleting category

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nice catch!

onyxData.optimisticData?.push({
Comment thread
tienifr marked this conversation as resolved.
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
value: {
requiresCategory: false,
pendingFields: {
requiresCategory: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE,
},
},
});
onyxData.successData?.push({
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
value: {
pendingFields: {
requiresCategory: null,
},
},
});
onyxData.failureData?.push({
Comment thread
tienifr marked this conversation as resolved.
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
value: {
requiresCategory: policy?.requiresCategory,
pendingFields: {
requiresCategory: null,
},
},
});
}

const parameters = {
policyID,
Expand Down Expand Up @@ -3437,15 +3471,21 @@ function clearCategoryErrors(policyID: string, categoryName: string) {
}

function deleteWorkspaceCategories(policyID: string, categoryNamesToDelete: string[]) {
const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`];
const policyCategories = allPolicyCategories?.[`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`] ?? {};
const optimisticPolicyCategoriesData = categoryNamesToDelete.reduce<Record<string, Partial<PolicyCategory>>>((acc, categoryName) => {
acc[categoryName] = {pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE};
return acc;
}, {});
const shouldDisableRequiresCategory = !OptionsListUtils.hasEnabledOptions(
Object.values(policyCategories).filter((category) => !categoryNamesToDelete.includes(category.name) && category.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE),
);
const onyxData: OnyxData = {
optimisticData: [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`,
value: categoryNamesToDelete.reduce<Record<string, Partial<PolicyCategory>>>((acc, categoryName) => {
acc[categoryName] = {pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE};
return acc;
}, {}),
value: optimisticPolicyCategoriesData,
},
],
successData: [
Expand All @@ -3472,6 +3512,37 @@ function deleteWorkspaceCategories(policyID: string, categoryNamesToDelete: stri
},
],
};
if (shouldDisableRequiresCategory) {
onyxData.optimisticData?.push({
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
value: {
requiresCategory: false,
pendingFields: {
requiresCategory: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE,
},
},
});
onyxData.successData?.push({
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
value: {
pendingFields: {
requiresCategory: null,
},
},
});
onyxData.failureData?.push({
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
value: {
requiresCategory: policy?.requiresCategory,
pendingFields: {
requiresCategory: null,
},
},
});
}

const parameters = {
policyID,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import type {StackScreenProps} from '@react-navigation/stack';
import React from 'react';
import {View} from 'react-native';
import type {OnyxEntry} from 'react-native-onyx';
import {withOnyx} from 'react-native-onyx';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import OfflineWithFeedback from '@components/OfflineWithFeedback';
import ScreenWrapper from '@components/ScreenWrapper';
Expand All @@ -9,23 +11,32 @@ import Text from '@components/Text';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import {setWorkspaceRequiresCategory} from '@libs/actions/Policy';
import * as OptionsListUtils from '@libs/OptionsListUtils';
import type {SettingsNavigatorParamList} from '@navigation/types';
import AdminPolicyAccessOrNotFoundWrapper from '@pages/workspace/AdminPolicyAccessOrNotFoundWrapper';
import FeatureEnabledAccessOrNotFoundWrapper from '@pages/workspace/FeatureEnabledAccessOrNotFoundWrapper';
import PaidPolicyAccessOrNotFoundWrapper from '@pages/workspace/PaidPolicyAccessOrNotFoundWrapper';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type SCREENS from '@src/SCREENS';
import type * as OnyxTypes from '@src/types/onyx';

type WorkspaceCategoriesSettingsPageProps = StackScreenProps<SettingsNavigatorParamList, typeof SCREENS.WORKSPACE.CATEGORIES_SETTINGS>;
type WorkspaceCategoriesSettingsPageOnyxProps = {
/** Collection of categories attached to a policy */
policyCategories: OnyxEntry<OnyxTypes.PolicyCategories>;
};

function WorkspaceCategoriesSettingsPage({route}: WorkspaceCategoriesSettingsPageProps) {
type WorkspaceCategoriesSettingsPageProps = WorkspaceCategoriesSettingsPageOnyxProps & StackScreenProps<SettingsNavigatorParamList, typeof SCREENS.WORKSPACE.CATEGORIES_SETTINGS>;

function WorkspaceCategoriesSettingsPage({route, policyCategories}: WorkspaceCategoriesSettingsPageProps) {
const styles = useThemeStyles();
const {translate} = useLocalize();

const updateWorkspaceRequiresCategory = (value: boolean) => {
setWorkspaceRequiresCategory(route.params.policyID, value);
};

const hasEnabledOptions = OptionsListUtils.hasEnabledOptions(policyCategories ?? {});
return (
<AdminPolicyAccessOrNotFoundWrapper policyID={route.params.policyID}>
<PaidPolicyAccessOrNotFoundWrapper policyID={route.params.policyID}>
Expand Down Expand Up @@ -53,7 +64,7 @@ function WorkspaceCategoriesSettingsPage({route}: WorkspaceCategoriesSettingsPag
isOn={policy?.requiresCategory ?? false}
accessibilityLabel={translate('workspace.categories.requiresCategory')}
onToggle={updateWorkspaceRequiresCategory}
disabled={!policy?.areCategoriesEnabled}
disabled={!policy?.areCategoriesEnabled || !hasEnabledOptions}
/>
</View>
</View>
Expand All @@ -69,4 +80,8 @@ function WorkspaceCategoriesSettingsPage({route}: WorkspaceCategoriesSettingsPag

WorkspaceCategoriesSettingsPage.displayName = 'WorkspaceCategoriesSettingsPage';

export default WorkspaceCategoriesSettingsPage;
export default withOnyx<WorkspaceCategoriesSettingsPageProps, WorkspaceCategoriesSettingsPageOnyxProps>({
policyCategories: {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looks like we're back at it again 🙂 #37508 (comment)

Copy link
Copy Markdown
Contributor Author

@tienifr tienifr Apr 2, 2024

Choose a reason for hiding this comment

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

@getusha

Yeah. Previously, I said that:

It's much less performant since we need to connect to the entire policyCategories in Onyx. And anywhere we use such are categories enabled logic, there'll be this overhead and duplicated logic.

Meanwhile we can just use areCategoriesEnabled which is an existing field designed for this very purpose (more details #37508 (comment)). There's no overhead since the places that need this use case already have the policy object passed in.

It is because we can use areCategoriesEnabled in this case. But now, we only turn off the requiresCategories, so I think to address the issue, we still need to subscribe the policyCategories

key: ({route}) => `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${route.params.policyID}`,
},
})(WorkspaceCategoriesSettingsPage);
6 changes: 3 additions & 3 deletions src/types/onyx/PolicyTag.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type * as OnyxCommon from './OnyxCommon';

type PolicyTag = {
type PolicyTag = OnyxCommon.OnyxValueWithOfflineFeedback<{
/** Name of a Tag */
name: string;

Expand All @@ -13,9 +13,9 @@ type PolicyTag = {

/** A list of errors keyed by microtime */
errors?: OnyxCommon.Errors | null;
};
}>;

type PolicyTags = Record<string, OnyxCommon.OnyxValueWithOfflineFeedback<PolicyTag>>;
type PolicyTags = Record<string, PolicyTag>;

type PolicyTagList<T extends string = string> = Record<
T,
Expand Down