diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index ca44931e7e8e..2f86819d91c6 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -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); } /** diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index 3c34e823ac9a..2464d7aee722 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -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'; @@ -2872,27 +2873,29 @@ function createWorkspaceFromIOUPayment(iouReport: Report | EmptyObject): string function setWorkspaceCategoryEnabled(policyID: string, categoriesToUpdate: Record) { const policyCategories = allPolicyCategories?.[`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`] ?? {}; + const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]; + const optimisticPolicyCategoriesData = { + ...Object.keys(categoriesToUpdate).reduce((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((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: [ @@ -2938,6 +2941,37 @@ function setWorkspaceCategoryEnabled(policyID: string, categoriesToUpdate: Recor }, ], }; + 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, @@ -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>>((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>>((acc, categoryName) => { - acc[categoryName] = {pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE}; - return acc; - }, {}), + value: optimisticPolicyCategoriesData, }, ], successData: [ @@ -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, diff --git a/src/pages/workspace/categories/WorkspaceCategoriesSettingsPage.tsx b/src/pages/workspace/categories/WorkspaceCategoriesSettingsPage.tsx index 0ec937b19ba2..260b6b94d4dc 100644 --- a/src/pages/workspace/categories/WorkspaceCategoriesSettingsPage.tsx +++ b/src/pages/workspace/categories/WorkspaceCategoriesSettingsPage.tsx @@ -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'; @@ -9,16 +11,24 @@ 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; +type WorkspaceCategoriesSettingsPageOnyxProps = { + /** Collection of categories attached to a policy */ + policyCategories: OnyxEntry; +}; -function WorkspaceCategoriesSettingsPage({route}: WorkspaceCategoriesSettingsPageProps) { +type WorkspaceCategoriesSettingsPageProps = WorkspaceCategoriesSettingsPageOnyxProps & StackScreenProps; + +function WorkspaceCategoriesSettingsPage({route, policyCategories}: WorkspaceCategoriesSettingsPageProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -26,6 +36,7 @@ function WorkspaceCategoriesSettingsPage({route}: WorkspaceCategoriesSettingsPag setWorkspaceRequiresCategory(route.params.policyID, value); }; + const hasEnabledOptions = OptionsListUtils.hasEnabledOptions(policyCategories ?? {}); return ( @@ -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} /> @@ -69,4 +80,8 @@ function WorkspaceCategoriesSettingsPage({route}: WorkspaceCategoriesSettingsPag WorkspaceCategoriesSettingsPage.displayName = 'WorkspaceCategoriesSettingsPage'; -export default WorkspaceCategoriesSettingsPage; +export default withOnyx({ + policyCategories: { + key: ({route}) => `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${route.params.policyID}`, + }, +})(WorkspaceCategoriesSettingsPage); diff --git a/src/types/onyx/PolicyTag.ts b/src/types/onyx/PolicyTag.ts index f469ac7fff70..37e979fb58f6 100644 --- a/src/types/onyx/PolicyTag.ts +++ b/src/types/onyx/PolicyTag.ts @@ -1,6 +1,6 @@ import type * as OnyxCommon from './OnyxCommon'; -type PolicyTag = { +type PolicyTag = OnyxCommon.OnyxValueWithOfflineFeedback<{ /** Name of a Tag */ name: string; @@ -13,9 +13,9 @@ type PolicyTag = { /** A list of errors keyed by microtime */ errors?: OnyxCommon.Errors | null; -}; +}>; -type PolicyTags = Record>; +type PolicyTags = Record; type PolicyTagList = Record< T,