Skip to content

Commit

Permalink
Merge pull request #36553 from allroundexperts/fix-36170
Browse files Browse the repository at this point in the history
feat: add policyReportFields to the policy object directly
  • Loading branch information
thienlnam authored Mar 20, 2024
2 parents 2571c8d + fdac852 commit 4891fe3
Show file tree
Hide file tree
Showing 14 changed files with 157 additions and 127 deletions.
2 changes: 0 additions & 2 deletions src/ONYXKEYS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,6 @@ const ONYXKEYS = {
POLICY_TAGS: 'policyTags_',
POLICY_RECENTLY_USED_TAGS: 'nvp_recentlyUsedTags_',
OLD_POLICY_RECENTLY_USED_TAGS: 'policyRecentlyUsedTags_',
POLICY_REPORT_FIELDS: 'policyReportFields_',
WORKSPACE_INVITE_MEMBERS_DRAFT: 'workspaceInviteMembersDraft_',
WORKSPACE_INVITE_MESSAGE_DRAFT: 'workspaceInviteMessageDraft_',
REPORT: 'report_',
Expand Down Expand Up @@ -500,7 +499,6 @@ type OnyxCollectionValuesMapping = {
[ONYXKEYS.COLLECTION.POLICY_MEMBERS]: OnyxTypes.PolicyMembers;
[ONYXKEYS.COLLECTION.POLICY_MEMBERS_DRAFTS]: OnyxTypes.PolicyMember;
[ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES]: OnyxTypes.RecentlyUsedCategories;
[ONYXKEYS.COLLECTION.POLICY_REPORT_FIELDS]: OnyxTypes.PolicyReportFields;
[ONYXKEYS.COLLECTION.DEPRECATED_POLICY_MEMBER_LIST]: OnyxTypes.PolicyMembers;
[ONYXKEYS.COLLECTION.WORKSPACE_INVITE_MEMBERS_DRAFT]: OnyxTypes.InvitedEmailsToAccountIDs;
[ONYXKEYS.COLLECTION.WORKSPACE_INVITE_MESSAGE_DRAFT]: string;
Expand Down
16 changes: 7 additions & 9 deletions src/components/ReportActionItem/MoneyReportView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,11 @@ type MoneyReportViewProps = {
/** Policy that the report belongs to */
policy: OnyxEntry<Policy>;

/** Policy report fields */
policyReportFields: PolicyReportField[];

/** Whether we should display the horizontal rule below the component */
shouldShowHorizontalRule: boolean;
};

function MoneyReportView({report, policy, policyReportFields, shouldShowHorizontalRule}: MoneyReportViewProps) {
function MoneyReportView({report, policy, shouldShowHorizontalRule}: MoneyReportViewProps) {
const theme = useTheme();
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();
Expand All @@ -60,9 +57,9 @@ function MoneyReportView({report, policy, policyReportFields, shouldShowHorizont
];

const sortedPolicyReportFields = useMemo<PolicyReportField[]>((): PolicyReportField[] => {
const fields = ReportUtils.getAvailableReportFields(report, policyReportFields);
const fields = ReportUtils.getAvailableReportFields(report, Object.values(policy?.fieldList ?? {}));
return fields.sort(({orderWeight: firstOrderWeight}, {orderWeight: secondOrderWeight}) => firstOrderWeight - secondOrderWeight);
}, [policyReportFields, report]);
}, [policy, report]);

return (
<View style={[StyleUtils.getReportWelcomeContainerStyle(isSmallScreenWidth, true)]}>
Expand All @@ -75,13 +72,14 @@ function MoneyReportView({report, policy, policyReportFields, shouldShowHorizont
const isTitleField = ReportUtils.isReportFieldOfTypeTitle(reportField);
const fieldValue = isTitleField ? report.reportName : reportField.value ?? reportField.defaultValue;
const isFieldDisabled = ReportUtils.isReportFieldDisabled(report, reportField, policy);
const fieldKey = ReportUtils.getReportFieldKey(reportField.fieldID);

return (
<OfflineWithFeedback
pendingAction={report.pendingFields?.[reportField.fieldID]}
errors={report.errorFields?.[reportField.fieldID]}
pendingAction={report.pendingFields?.[fieldKey]}
errors={report.errorFields?.[fieldKey]}
errorRowStyles={styles.ph5}
key={`menuItem-${reportField.fieldID}`}
key={`menuItem-${fieldKey}`}
>
<MenuItemWithTopDescription
description={Str.UCFirst(reportField.name)}
Expand Down
57 changes: 40 additions & 17 deletions src/libs/ReportUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import type {
PersonalDetailsList,
Policy,
PolicyReportField,
PolicyReportFields,
Report,
ReportAction,
ReportMetadata,
Expand Down Expand Up @@ -482,14 +481,6 @@ Onyx.connect({
callback: (value) => (allPolicies = value),
});

let allPolicyReportFields: OnyxCollection<PolicyReportFields> = {};

Onyx.connect({
key: ONYXKEYS.COLLECTION.POLICY_REPORT_FIELDS,
waitForCollectionCallback: true,
callback: (value) => (allPolicyReportFields = value),
});

let allBetas: OnyxEntry<Beta[]>;
Onyx.connect({
key: ONYXKEYS.BETAS,
Expand Down Expand Up @@ -2099,22 +2090,36 @@ function isReportFieldDisabled(report: OnyxEntry<Report>, reportField: OnyxEntry
/**
* Given a set of report fields, return the field of type formula
*/
function getFormulaTypeReportField(reportFields: PolicyReportFields) {
return Object.values(reportFields).find((field) => field.type === 'formula');
function getFormulaTypeReportField(reportFields: Record<string, PolicyReportField>) {
return Object.values(reportFields).find((field) => field?.type === 'formula');
}

/**
* Given a set of report fields, return the field that refers to title
*/
function getTitleReportField(reportFields: PolicyReportFields) {
function getTitleReportField(reportFields: Record<string, PolicyReportField>) {
return Object.values(reportFields).find((field) => isReportFieldOfTypeTitle(field));
}

/**
* Get the key for a report field
*/
function getReportFieldKey(reportFieldId: string) {
return `expensify_${reportFieldId}`;
}

/**
* Get the report fields attached to the policy given policyID
*/
function getReportFieldsByPolicyID(policyID: string) {
return Object.entries(allPolicyReportFields ?? {}).find(([key]) => key.replace(ONYXKEYS.COLLECTION.POLICY_REPORT_FIELDS, '') === policyID)?.[1];
function getReportFieldsByPolicyID(policyID: string): Record<string, PolicyReportField> {
const policyReportFields = Object.entries(allPolicies ?? {}).find(([key]) => key.replace(ONYXKEYS.COLLECTION.POLICY, '') === policyID);
const fieldList = policyReportFields?.[1]?.fieldList;

if (!policyReportFields || !fieldList) {
return {};
}

return fieldList;
}

/**
Expand All @@ -2123,7 +2128,7 @@ function getReportFieldsByPolicyID(policyID: string) {

function getAvailableReportFields(report: Report, policyReportFields: PolicyReportField[]): PolicyReportField[] {
// Get the report fields that are attached to a report. These will persist even if a field is deleted from the policy.
const reportFields = Object.values(report.reportFields ?? {});
const reportFields = Object.values(report.fieldList ?? {});
const reportIsSettled = isSettled(report.reportID);

// If the report is settled, we don't want to show any new field that gets added to the policy.
Expand All @@ -2134,15 +2139,32 @@ function getAvailableReportFields(report: Report, policyReportFields: PolicyRepo
// If the report is unsettled, we want to merge the new fields that get added to the policy with the fields that
// are attached to the report.
const mergedFieldIds = Array.from(new Set([...policyReportFields.map(({fieldID}) => fieldID), ...reportFields.map(({fieldID}) => fieldID)]));
return mergedFieldIds.map((id) => report?.reportFields?.[id] ?? policyReportFields.find(({fieldID}) => fieldID === id)) as PolicyReportField[];

const fields = mergedFieldIds.map((id) => {
const field = report?.fieldList?.[getReportFieldKey(id)];

if (field) {
return field;
}

const policyReportField = policyReportFields.find(({fieldID}) => fieldID === id);

if (policyReportField) {
return policyReportField;
}

return null;
});

return fields.filter(Boolean) as PolicyReportField[];
}

/**
* Get the title for an IOU or expense chat which will be showing the payer and the amount
*/
function getMoneyRequestReportName(report: OnyxEntry<Report>, policy: OnyxEntry<Policy> | undefined = undefined): string {
const isReportSettled = isSettled(report?.reportID ?? '');
const reportFields = isReportSettled ? report?.reportFields : getReportFieldsByPolicyID(report?.policyID ?? '');
const reportFields = isReportSettled ? report?.fieldList : getReportFieldsByPolicyID(report?.policyID ?? '');
const titleReportField = getFormulaTypeReportField(reportFields ?? {});

if (titleReportField && report?.reportName && reportFieldsEnabled(report)) {
Expand Down Expand Up @@ -5623,6 +5645,7 @@ export {
hasUpdatedTotal,
isReportFieldDisabled,
getAvailableReportFields,
getReportFieldKey,
reportFieldsEnabled,
getAllAncestorReportActionIDs,
getPendingChatMembers,
Expand Down
29 changes: 15 additions & 14 deletions src/libs/actions/Report.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1589,29 +1589,30 @@ function updateReportName(reportID: string, value: string, previousValue: string
}

function updateReportField(reportID: string, reportField: PolicyReportField, previousReportField: PolicyReportField) {
const recentlyUsedValues = allRecentlyUsedReportFields?.[reportField.fieldID] ?? [];
const fieldKey = ReportUtils.getReportFieldKey(reportField.fieldID);
const recentlyUsedValues = allRecentlyUsedReportFields?.[fieldKey] ?? [];

const optimisticData: OnyxUpdate[] = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`,
value: {
reportFields: {
[reportField.fieldID]: reportField,
fieldList: {
[fieldKey]: reportField,
},
pendingFields: {
[reportField.fieldID]: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE,
[fieldKey]: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE,
},
},
},
];

if (reportField.type === 'dropdown') {
if (reportField.type === 'dropdown' && reportField.value) {
optimisticData.push({
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.RECENTLY_USED_REPORT_FIELDS,
value: {
[reportField.fieldID]: [...new Set([...recentlyUsedValues, reportField.value])],
[fieldKey]: [...new Set([...recentlyUsedValues, reportField.value])],
},
});
}
Expand All @@ -1621,14 +1622,14 @@ function updateReportField(reportID: string, reportField: PolicyReportField, pre
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`,
value: {
reportFields: {
[reportField.fieldID]: previousReportField,
fieldList: {
[fieldKey]: previousReportField,
},
pendingFields: {
[reportField.fieldID]: null,
[fieldKey]: null,
},
errorFields: {
[reportField.fieldID]: ErrorUtils.getMicroSecondOnyxError('report.genericUpdateReportFieldFailureMessage'),
[fieldKey]: ErrorUtils.getMicroSecondOnyxError('report.genericUpdateReportFieldFailureMessage'),
},
},
},
Expand All @@ -1639,7 +1640,7 @@ function updateReportField(reportID: string, reportField: PolicyReportField, pre
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.RECENTLY_USED_REPORT_FIELDS,
value: {
[reportField.fieldID]: recentlyUsedValues,
[fieldKey]: recentlyUsedValues,
},
});
}
Expand All @@ -1650,18 +1651,18 @@ function updateReportField(reportID: string, reportField: PolicyReportField, pre
key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`,
value: {
pendingFields: {
[reportField.fieldID]: null,
[fieldKey]: null,
},
errorFields: {
[reportField.fieldID]: null,
[fieldKey]: null,
},
},
},
];

const parameters = {
reportID,
reportFields: JSON.stringify({[`expensify_${reportField.fieldID}`]: reportField}),
reportFields: JSON.stringify({[fieldKey]: reportField}),
};

API.write(WRITE_COMMANDS.SET_REPORT_FIELD, parameters, {optimisticData, failureData, successData});
Expand Down
16 changes: 8 additions & 8 deletions src/pages/EditReportFieldDatePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ type EditReportFieldDatePageProps = {
/** Name of the policy report field */
fieldName: string;

/** ID of the policy report field */
fieldID: string;
/** Key of the policy report field */
fieldKey: string;

/** Flag to indicate if the field can be left blank */
isRequired: boolean;
Expand All @@ -29,20 +29,20 @@ type EditReportFieldDatePageProps = {
onSubmit: (form: FormOnyxValues<typeof ONYXKEYS.FORMS.REPORT_FIELD_EDIT_FORM>) => void;
};

function EditReportFieldDatePage({fieldName, isRequired, onSubmit, fieldValue, fieldID}: EditReportFieldDatePageProps) {
function EditReportFieldDatePage({fieldName, isRequired, onSubmit, fieldValue, fieldKey}: EditReportFieldDatePageProps) {
const styles = useThemeStyles();
const {translate} = useLocalize();
const inputRef = useRef<AnimatedTextInputRef>(null);

const validate = useCallback(
(value: FormOnyxValues<typeof ONYXKEYS.FORMS.REPORT_FIELD_EDIT_FORM>) => {
const errors: FormInputErrors<typeof ONYXKEYS.FORMS.REPORT_FIELD_EDIT_FORM> = {};
if (isRequired && value[fieldID].trim() === '') {
errors[fieldID] = 'common.error.fieldRequired';
if (isRequired && value[fieldKey].trim() === '') {
errors[fieldKey] = 'common.error.fieldRequired';
}
return errors;
},
[fieldID, isRequired],
[fieldKey, isRequired],
);

return (
Expand All @@ -67,8 +67,8 @@ function EditReportFieldDatePage({fieldName, isRequired, onSubmit, fieldValue, f
{/* @ts-expect-error TODO: Remove this once DatePicker (https://github.com/Expensify/App/issues/25148) is migrated to TypeScript. */}
<InputWrapper<unknown>
InputComponent={DatePicker}
inputID={fieldID}
name={fieldID}
inputID={fieldKey}
name={fieldKey}
defaultValue={fieldValue}
label={fieldName}
accessibilityLabel={fieldName}
Expand Down
14 changes: 9 additions & 5 deletions src/pages/EditReportFieldDropdownPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ type EditReportFieldDropdownPageComponentProps = {
/** Name of the policy report field */
fieldName: string;

/** ID of the policy report field */
fieldID: string;
/** Key of the policy report field */
fieldKey: string;

/** ID of the policy this report field belongs to */
// eslint-disable-next-line react/no-unused-prop-types
Expand All @@ -37,12 +37,12 @@ type EditReportFieldDropdownPageOnyxProps = {

type EditReportFieldDropdownPageProps = EditReportFieldDropdownPageComponentProps & EditReportFieldDropdownPageOnyxProps;

function EditReportFieldDropdownPage({fieldName, onSubmit, fieldID, fieldValue, fieldOptions, recentlyUsedReportFields}: EditReportFieldDropdownPageProps) {
function EditReportFieldDropdownPage({fieldName, onSubmit, fieldKey, fieldValue, fieldOptions, recentlyUsedReportFields}: EditReportFieldDropdownPageProps) {
const [searchValue, setSearchValue] = useState('');
const styles = useThemeStyles();
const {getSafeAreaMargins} = useStyleUtils();
const {translate} = useLocalize();
const recentlyUsedOptions = useMemo(() => recentlyUsedReportFields?.[fieldID] ?? [], [recentlyUsedReportFields, fieldID]);
const recentlyUsedOptions = useMemo(() => recentlyUsedReportFields?.[fieldKey] ?? [], [recentlyUsedReportFields, fieldKey]);
const [headerMessage, setHeaderMessage] = useState('');

const sections = useMemo(() => {
Expand Down Expand Up @@ -93,7 +93,11 @@ function EditReportFieldDropdownPage({fieldName, onSubmit, fieldID, fieldValue,
boldStyle
sections={sections}
value={searchValue}
onSelectRow={(option: Record<string, string>) => onSubmit({[fieldID]: option.text})}
onSelectRow={(option: Record<string, string>) =>
onSubmit({
[fieldKey]: fieldValue === option.text ? '' : option.text,
})
}
onChangeText={setSearchValue}
highlightSelectedOptions
isRowMultilineSupported
Expand Down
Loading

0 comments on commit 4891fe3

Please sign in to comment.