Skip to content

Commit

Permalink
Merge pull request #44729 from waterim/feat-43687-reportFields-bulk
Browse files Browse the repository at this point in the history
Feature: Implement support for bulk operations on the policy report fields
  • Loading branch information
mountiny authored Jul 9, 2024
2 parents 1b9ed26 + 1c4b7e9 commit 04581f1
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 21 deletions.
4 changes: 3 additions & 1 deletion src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2622,7 +2622,9 @@ export default {
reportFields: {
addField: 'Add field',
delete: 'Delete field',
deleteConfirmation: 'Are you sure that you want to delete this field?',
deleteFields: 'Delete fields',
deleteConfirmation: 'Are you sure you want to delete this report field?',
deleteFieldsConfirmation: 'Are you sure you want to delete these report fields?',
emptyReportFields: {
title: "You haven't created any report fields",
subtitle: 'Add a custom field (text, date, or dropdown) that appears on reports.',
Expand Down
6 changes: 4 additions & 2 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2667,8 +2667,10 @@ export default {
},
reportFields: {
addField: 'Añadir campo',
delete: 'Eliminar campos',
deleteConfirmation: '¿Estás seguro de que quieres eliminar esta campos?',
delete: 'Eliminar campo',
deleteFields: 'Eliminar campos',
deleteConfirmation: '¿Está seguro de que desea eliminar este campo del informe?',
deleteFieldsConfirmation: '¿Está seguro de que desea eliminar estos campos del informe?',
emptyReportFields: {
title: 'No has creado ningún campo de informe',
subtitle: 'Añade un campo personalizado (texto, fecha o desplegable) que aparezca en los informes.',
Expand Down
4 changes: 2 additions & 2 deletions src/libs/API/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ const WRITE_COMMANDS = {
RENAME_POLICY_TAG: 'RenamePolicyTag',
SET_WORKSPACE_REQUIRES_CATEGORY: 'SetWorkspaceRequiresCategory',
DELETE_WORKSPACE_CATEGORIES: 'DeleteWorkspaceCategories',
POLICY_REPORT_FIELDS_REPLACE: 'Policy_ReportFields_Replace',
DELETE_POLICY_REPORT_FIELD: 'DeletePolicyReportField',
SET_POLICY_TAGS_REQUIRED: 'SetPolicyTagsRequired',
SET_POLICY_REQUIRES_TAG: 'SetPolicyRequiresTag',
RENAME_POLICY_TAG_LIST: 'RenamePolicyTaglist',
Expand Down Expand Up @@ -415,7 +415,7 @@ type WriteCommandParameters = {
[WRITE_COMMANDS.RENAME_WORKSPACE_CATEGORY]: Parameters.RenameWorkspaceCategoriesParams;
[WRITE_COMMANDS.SET_WORKSPACE_REQUIRES_CATEGORY]: Parameters.SetWorkspaceRequiresCategoryParams;
[WRITE_COMMANDS.DELETE_WORKSPACE_CATEGORIES]: Parameters.DeleteWorkspaceCategoriesParams;
[WRITE_COMMANDS.POLICY_REPORT_FIELDS_REPLACE]: Parameters.PolicyReportFieldsReplace;
[WRITE_COMMANDS.DELETE_POLICY_REPORT_FIELD]: Parameters.PolicyReportFieldsReplace;
[WRITE_COMMANDS.SET_POLICY_REQUIRES_TAG]: Parameters.SetPolicyRequiresTag;
[WRITE_COMMANDS.SET_POLICY_TAGS_REQUIRED]: Parameters.SetPolicyTagsRequired;
[WRITE_COMMANDS.RENAME_POLICY_TAG_LIST]: Parameters.RenamePolicyTaglistParams;
Expand Down
2 changes: 1 addition & 1 deletion src/libs/actions/Policy/ReportField.ts
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ function deleteReportFields(policyID: string, reportFieldsToUpdate: string[]) {
reportFields: JSON.stringify(Object.values(updatedReportFields)),
};

API.write(WRITE_COMMANDS.POLICY_REPORT_FIELDS_REPLACE, parameters, onyxData);
API.write(WRITE_COMMANDS.DELETE_POLICY_REPORT_FIELD, parameters, onyxData);
}

/**
Expand Down
88 changes: 73 additions & 15 deletions src/pages/workspace/reportFields/WorkspaceReportFieldsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import React, {useEffect, useMemo, useState} from 'react';
import {ActivityIndicator, View} from 'react-native';
import {useOnyx} from 'react-native-onyx';
import Button from '@components/Button';
import ButtonWithDropdownMenu from '@components/ButtonWithDropdownMenu';
import type {DropdownOption} from '@components/ButtonWithDropdownMenu/types';
import ConfirmModal from '@components/ConfirmModal';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import * as Expensicons from '@components/Icon/Expensicons';
import * as Illustrations from '@components/Icon/Illustrations';
Expand All @@ -16,6 +19,7 @@ import type {ListItem} from '@components/SelectionList/types';
import Text from '@components/Text';
import WorkspaceEmptyStateSection from '@components/WorkspaceEmptyStateSection';
import useLocalize from '@hooks/useLocalize';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
Expand All @@ -24,11 +28,13 @@ import Navigation from '@libs/Navigation/Navigation';
import type {FullScreenNavigatorParamList} from '@libs/Navigation/types';
import * as ReportUtils from '@libs/ReportUtils';
import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper';
import * as ReportField from '@userActions/Policy/ReportField';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type SCREENS from '@src/SCREENS';
import type {PolicyReportField} from '@src/types/onyx/Policy';
import type DeepValueOf from '@src/types/utils/DeepValueOf';

type ReportFieldForList = ListItem & {
value: string;
Expand All @@ -43,6 +49,7 @@ function WorkspaceReportFieldsPage({
params: {policyID},
},
}: WorkspaceReportFieldsPageProps) {
const {shouldUseNarrowLayout} = useResponsiveLayout();
const {isSmallScreenWidth} = useWindowDimensions();
const styles = useThemeStyles();
const theme = useTheme();
Expand All @@ -57,6 +64,7 @@ function WorkspaceReportFieldsPage({
return Object.fromEntries(Object.entries(policy.fieldList).filter(([_, value]) => value.fieldID !== 'text_title'));
}, [policy]);
const [selectedReportFields, setSelectedReportFields] = useState<PolicyReportField[]>([]);
const [deleteReportFieldsConfirmModalVisible, setDeleteReportFieldsConfirmModalVisible] = useState(false);

useEffect(() => {
if (isFocused) {
Expand All @@ -79,6 +87,7 @@ function WorkspaceReportFieldsPage({
orderWeight: reportField.orderWeight,
pendingAction: reportField.pendingAction,
isSelected: selectedReportFields.find((selectedReportField) => selectedReportField.name === reportField.name) !== undefined,
isDisabled: reportField.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE,
text: reportField.name,
rightElement: (
<ListItemRightCaretWithLabel
Expand All @@ -100,25 +109,64 @@ function WorkspaceReportFieldsPage({
setSelectedReportFields(updatedReportFields);
};

const toggleAllReportFields = () => {
const availableReportFields = Object.values(filteredPolicyFieldList).filter((reportField) => reportField.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE);
const isAllSelected = availableReportFields.length === selectedReportFields.length;
setSelectedReportFields(isAllSelected ? [] : availableReportFields);
};

const navigateToReportFieldSettings = (reportField: ReportFieldForList) => {
Navigation.navigate(ROUTES.WORKSPACE_REPORT_FIELD_SETTINGS.getRoute(policyID, reportField.fieldID));
};

const handleDeleteReportFields = () => {
const reportFieldKeys = selectedReportFields.map((selectedReportField) => ReportUtils.getReportFieldKey(selectedReportField.fieldID));
setSelectedReportFields([]);
ReportField.deleteReportFields(policyID, reportFieldKeys);
setDeleteReportFieldsConfirmModalVisible(false);
};

const isLoading = policy === undefined;
const shouldShowEmptyState = Object.values(filteredPolicyFieldList).length <= 0 && !isLoading;

const getHeaderButtons = () => (
<View style={[styles.w100, styles.flexRow, styles.gap2, isSmallScreenWidth && styles.mb3]}>
<Button
medium
success
onPress={() => Navigation.navigate(ROUTES.WORKSPACE_CREATE_REPORT_FIELD.getRoute(policyID))}
icon={Expensicons.Plus}
text={translate('workspace.reportFields.addField')}
style={isSmallScreenWidth && styles.flex1}
/>
</View>
);
const shouldShowEmptyState =
Object.values(filteredPolicyFieldList).filter((reportField) => reportField.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE).length <= 0 && !isLoading;

const getHeaderButtons = () => {
const options: Array<DropdownOption<DeepValueOf<typeof CONST.POLICY.BULK_ACTION_TYPES>>> = [];

if (selectedReportFields.length > 0) {
options.push({
icon: Expensicons.Trashcan,
text: translate(selectedReportFields.length === 1 ? 'workspace.reportFields.delete' : 'workspace.reportFields.deleteFields'),
value: CONST.POLICY.BULK_ACTION_TYPES.DELETE,
onSelected: () => setDeleteReportFieldsConfirmModalVisible(true),
});

return (
<ButtonWithDropdownMenu
onPress={() => null}
shouldAlwaysShowDropdownMenu
pressOnEnter
buttonSize={CONST.DROPDOWN_BUTTON_SIZE.MEDIUM}
customText={translate('workspace.common.selected', {selectedNumber: selectedReportFields.length})}
options={options}
isSplitButton={false}
style={[shouldUseNarrowLayout && styles.flexGrow1, shouldUseNarrowLayout && styles.mb3]}
/>
);
}
return (
<View style={[styles.w100, styles.flexRow, styles.gap2, isSmallScreenWidth && styles.mb3]}>
<Button
medium
success
onPress={() => Navigation.navigate(ROUTES.WORKSPACE_CREATE_REPORT_FIELD.getRoute(policyID))}
icon={Expensicons.Plus}
text={translate('workspace.reportFields.addField')}
style={[isSmallScreenWidth && styles.flex1]}
/>
</View>
);
};

const getCustomListHeader = () => (
<View style={[styles.flex1, styles.flexRow, styles.justifyContentBetween, styles.pl3]}>
Expand Down Expand Up @@ -154,6 +202,16 @@ function WorkspaceReportFieldsPage({
{!isSmallScreenWidth && getHeaderButtons()}
</HeaderWithBackButton>
{isSmallScreenWidth && <View style={[styles.pl5, styles.pr5]}>{getHeaderButtons()}</View>}
<ConfirmModal
isVisible={deleteReportFieldsConfirmModalVisible}
onConfirm={handleDeleteReportFields}
onCancel={() => setDeleteReportFieldsConfirmModalVisible(false)}
title={translate(selectedReportFields.length === 1 ? 'workspace.reportFields.delete' : 'workspace.reportFields.deleteFields')}
prompt={translate(selectedReportFields.length === 1 ? 'workspace.reportFields.deleteConfirmation' : 'workspace.reportFields.deleteFieldsConfirmation')}
confirmText={translate('common.delete')}
cancelText={translate('common.cancel')}
danger
/>
{(!isSmallScreenWidth || reportFieldsSections[0].data.length === 0 || isLoading) && getHeaderText()}
{isLoading && (
<ActivityIndicator
Expand All @@ -175,7 +233,7 @@ function WorkspaceReportFieldsPage({
sections={reportFieldsSections}
onCheckboxPress={updateSelectedReportFields}
onSelectRow={navigateToReportFieldSettings}
onSelectAll={() => {}}
onSelectAll={toggleAllReportFields}
ListItem={TableListItem}
customListHeader={getCustomListHeader()}
listHeaderContent={isSmallScreenWidth ? getHeaderText() : null}
Expand Down

0 comments on commit 04581f1

Please sign in to comment.