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

Add toggle for enable/disable instead of label #55192

Merged
7 changes: 2 additions & 5 deletions src/libs/actions/Policy/Category.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import lodashCloneDeep from 'lodash/cloneDeep';
import lodashUnion from 'lodash/union';
import type {NullishDeep, OnyxCollection, OnyxUpdate} from 'react-native-onyx';
import Onyx from 'react-native-onyx';
import type {PartialDeep} from 'type-fest';
import * as API from '@libs/API';
import type {
EnablePolicyCategoriesParams,
Expand Down Expand Up @@ -215,7 +216,6 @@ function setWorkspaceCategoryEnabled(policyID: string, categoriesToUpdate: Recor
const optimisticPolicyCategoriesData = {
...Object.keys(categoriesToUpdate).reduce<PolicyCategories>((acc, key) => {
acc[key] = {
...policyCategories[key],
...categoriesToUpdate[key],
errors: null,
pendingFields: {
Expand All @@ -241,10 +241,8 @@ function setWorkspaceCategoryEnabled(policyID: string, categoriesToUpdate: Recor
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`,
value: {
...Object.keys(categoriesToUpdate).reduce<PolicyCategories>((acc, key) => {
...Object.keys(categoriesToUpdate).reduce<PartialDeep<PolicyCategories>>((acc, key) => {
acc[key] = {
...policyCategories[key],
...categoriesToUpdate[key],
errors: null,
pendingFields: {
enabled: null,
Expand All @@ -265,7 +263,6 @@ function setWorkspaceCategoryEnabled(policyID: string, categoriesToUpdate: Recor
...Object.keys(categoriesToUpdate).reduce<PolicyCategories>((acc, key) => {
acc[key] = {
...policyCategories[key],
...categoriesToUpdate[key],
errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('workspace.categories.updateFailureMessage'),
pendingFields: {
enabled: null,
Expand Down
20 changes: 17 additions & 3 deletions src/pages/workspace/categories/WorkspaceCategoriesPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ import * as Expensicons from '@components/Icon/Expensicons';
import * as Illustrations from '@components/Icon/Illustrations';
import LottieAnimations from '@components/LottieAnimations';
import ScreenWrapper from '@components/ScreenWrapper';
import ListItemRightCaretWithLabel from '@components/SelectionList/ListItemRightCaretWithLabel';
import TableListItem from '@components/SelectionList/TableListItem';
import type {ListItem} from '@components/SelectionList/types';
import SelectionListWithModal from '@components/SelectionListWithModal';
import CustomListHeader from '@components/SelectionListWithModal/CustomListHeader';
import TableListItemSkeleton from '@components/Skeletons/TableRowSkeleton';
import Switch from '@components/Switch';
import Text from '@components/Text';
import TextLink from '@components/TextLink';
import useAutoTurnSelectionModeOffWhenHasNoActiveOption from '@hooks/useAutoTurnSelectionModeOffWhenHasNoActiveOption';
Expand Down Expand Up @@ -104,6 +104,13 @@ function WorkspaceCategoriesPage({route}: WorkspaceCategoriesPageProps) {
setSelectedCategories({});
}, [isFocused]);

const updateWorkspaceRequiresCategory = useCallback(
(value: boolean, categoryName: string) => {
setWorkspaceCategoryEnabled(policyId, {[categoryName]: {name: categoryName, enabled: value}});
},
[policyId],
);

const categoryList = useMemo<PolicyOption[]>(() => {
const categories = lodashSortBy(Object.values(policyCategories ?? {}), 'name', localeCompare) as PolicyCategory[];
return categories.reduce<PolicyOption[]>((acc, value) => {
Expand All @@ -120,12 +127,19 @@ function WorkspaceCategoriesPage({route}: WorkspaceCategoriesPageProps) {
isDisabled,
pendingAction: value.pendingAction,
errors: value.errors ?? undefined,
rightElement: <ListItemRightCaretWithLabel labelText={value.enabled ? translate('workspace.common.enabled') : translate('workspace.common.disabled')} />,
rightElement: (
<Switch
isOn={value.enabled}
disabled={isDisabled}
accessibilityLabel={translate('workspace.categories.enableCategory')}
onToggle={(newValue: boolean) => updateWorkspaceRequiresCategory(newValue, value.name)}
/>
),
});

return acc;
}, []);
}, [policyCategories, isOffline, selectedCategories, canSelectMultiple, translate]);
}, [policyCategories, isOffline, selectedCategories, canSelectMultiple, translate, updateWorkspaceRequiresCategory]);

useAutoTurnSelectionModeOffWhenHasNoActiveOption(categoryList);

Expand Down
80 changes: 63 additions & 17 deletions src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton';
import * as Expensicons from '@components/Icon/Expensicons';
import * as Illustrations from '@components/Icon/Illustrations';
import ScreenWrapper from '@components/ScreenWrapper';
import ListItemRightCaretWithLabel from '@components/SelectionList/ListItemRightCaretWithLabel';
import TableListItem from '@components/SelectionList/TableListItem';
import type {ListItem} from '@components/SelectionList/types';
import SelectionListWithModal from '@components/SelectionListWithModal';
import CustomListHeader from '@components/SelectionListWithModal/CustomListHeader';
import Switch from '@components/Switch';
import Text from '@components/Text';
import useLocalize from '@hooks/useLocalize';
import useMobileSelectionMode from '@hooks/useMobileSelectionMode';
Expand All @@ -22,14 +22,20 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import {turnOffMobileSelectionMode} from '@libs/actions/MobileSelectionMode';
import * as CurrencyUtils from '@libs/CurrencyUtils';
import * as DeviceCapabilities from '@libs/DeviceCapabilities';
import {
clearCreateDistanceRateItemAndError,
clearDeleteDistanceRateError,
deletePolicyDistanceRates,
openPolicyDistanceRatesPage,
setPolicyDistanceRatesEnabled,
} from '@libs/actions/Policy/DistanceRate';
import {convertAmountToDisplayString} from '@libs/CurrencyUtils';
import {canUseTouchScreen} from '@libs/DeviceCapabilities';
import Navigation from '@libs/Navigation/Navigation';
import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types';
import {getDistanceRateCustomUnit} from '@libs/PolicyUtils';
import type {FullScreenNavigatorParamList} from '@navigation/types';
import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper';
import * as DistanceRate from '@userActions/Policy/DistanceRate';
import ButtonWithDropdownMenu from '@src/components/ButtonWithDropdownMenu';
import CONST from '@src/CONST';
import ROUTES from '@src/ROUTES';
Expand Down Expand Up @@ -68,17 +74,21 @@ function PolicyDistanceRatesPage({
);

const fetchDistanceRates = useCallback(() => {
DistanceRate.openPolicyDistanceRatesPage(policyID);
openPolicyDistanceRatesPage(policyID);
}, [policyID]);

const dismissError = useCallback(
(item: RateForList) => {
if (!customUnit?.customUnitID) {
return;
}

if (customUnitRates[item.value].errors) {
DistanceRate.clearDeleteDistanceRateError(policyID, customUnit?.customUnitID ?? '', item.value);
clearDeleteDistanceRateError(policyID, customUnit.customUnitID, item.value);
return;
}

DistanceRate.clearCreateDistanceRateItemAndError(policyID, customUnit?.customUnitID ?? '', item.value);
clearCreateDistanceRateItemAndError(policyID, customUnit.customUnitID, item.value);
},
[customUnit?.customUnitID, customUnitRates, policyID],
);
Expand All @@ -98,16 +108,45 @@ function PolicyDistanceRatesPage({
setSelectedDistanceRates([]);
}, [isFocused]);

const updateDistanceRateEnabled = useCallback(
(value: boolean, rateID: string) => {
if (!customUnit) {
return;
}
const rate = customUnit?.rates?.[rateID];
// Rates can be disabled or deleted as long as in the remaining rates there is always at least one enabled rate and there are no pending delete actions
const canDisableOrDeleteRate = Object.values(customUnit?.rates ?? {}).some(
(distanceRate: Rate) => distanceRate?.enabled && rateID !== distanceRate?.customUnitRateID && distanceRate?.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE,
);

if (!rate?.enabled || canDisableOrDeleteRate) {
setSelectedDistanceRates((prevSelectedRates) =>
prevSelectedRates.map((selectedRate) => {
if (selectedRate.customUnitRateID === rateID) {
return {...selectedRate, enabled: value};
}
return selectedRate;
}),
);

setPolicyDistanceRatesEnabled(policyID, customUnit, [{...rate, enabled: value}]);
} else {
setIsWarningModalVisible(true);
}
},
[customUnit, policyID],
);

const distanceRatesList = useMemo<RateForList[]>(
() =>
Object.values(customUnitRates)
.sort((rateA, rateB) => (rateA?.rate ?? 0) - (rateB?.rate ?? 0))
.map((value) => ({
value: value.customUnitRateID ?? '',
text: `${CurrencyUtils.convertAmountToDisplayString(value.rate, value.currency ?? CONST.CURRENCY.USD)} / ${translate(
value: value.customUnitRateID,
text: `${convertAmountToDisplayString(value.rate, value.currency ?? CONST.CURRENCY.USD)} / ${translate(
`common.${customUnit?.attributes?.unit ?? CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES}`,
)}`,
keyForList: value.customUnitRateID ?? '',
keyForList: value.customUnitRateID,
isSelected: selectedDistanceRates.find((rate) => rate.customUnitRateID === value.customUnitRateID) !== undefined && canSelectMultiple,
isDisabled: value.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE,
pendingAction:
Expand All @@ -119,9 +158,16 @@ function PolicyDistanceRatesPage({
value.pendingFields?.taxClaimablePercentage ??
(policy?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD ? policy?.pendingAction : undefined),
errors: value.errors ?? undefined,
rightElement: <ListItemRightCaretWithLabel labelText={value.enabled ? translate('workspace.common.enabled') : translate('workspace.common.disabled')} />,
rightElement: (
<Switch
isOn={!!value?.enabled}
accessibilityLabel={translate('workspace.distanceRates.trackTax')}
onToggle={(newValue: boolean) => updateDistanceRateEnabled(newValue, value.customUnitRateID)}
disabled={value.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE}
/>
),
})),
[customUnit?.attributes?.unit, customUnitRates, selectedDistanceRates, translate, policy?.pendingAction, canSelectMultiple],
[customUnitRates, translate, customUnit, selectedDistanceRates, canSelectMultiple, policy?.pendingAction, updateDistanceRateEnabled],
);

const addRate = () => {
Expand All @@ -141,7 +187,7 @@ function PolicyDistanceRatesPage({
return;
}

DistanceRate.setPolicyDistanceRatesEnabled(
setPolicyDistanceRatesEnabled(
policyID,
customUnit,
selectedDistanceRates.filter((rate) => rate.enabled).map((rate) => ({...rate, enabled: false})),
Expand All @@ -154,7 +200,7 @@ function PolicyDistanceRatesPage({
return;
}

DistanceRate.setPolicyDistanceRatesEnabled(
setPolicyDistanceRatesEnabled(
policyID,
customUnit,
selectedDistanceRates.filter((rate) => !rate.enabled).map((rate) => ({...rate, enabled: true})),
Expand All @@ -167,10 +213,10 @@ function PolicyDistanceRatesPage({
return;
}

DistanceRate.deletePolicyDistanceRates(
deletePolicyDistanceRates(
policyID,
customUnit,
selectedDistanceRates.map((rate) => rate.customUnitRateID ?? ''),
selectedDistanceRates.map((rate) => rate.customUnitRateID),
);
setSelectedDistanceRates([]);
setIsDeleteModalVisible(false);
Expand Down Expand Up @@ -329,7 +375,7 @@ function PolicyDistanceRatesPage({
onSelectAll={toggleAllRates}
onDismissError={dismissError}
ListItem={TableListItem}
shouldPreventDefaultFocusOnSelectRow={!DeviceCapabilities.canUseTouchScreen()}
shouldPreventDefaultFocusOnSelectRow={!canUseTouchScreen()}
customListHeader={getCustomListHeader()}
listHeaderWrapperStyle={[styles.ph9, styles.pv3, styles.pb5]}
listHeaderContent={shouldUseNarrowLayout ? getHeaderText() : null}
Expand Down
Loading