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

feat: add policyReportFields to the policy object directly #36553

Merged
merged 16 commits into from
Mar 20, 2024
Merged
Show file tree
Hide file tree
Changes from 12 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: 0 additions & 2 deletions src/ONYXKEYS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,6 @@ const ONYXKEYS = {
POLICY_RECENTLY_USED_CATEGORIES: 'policyRecentlyUsedCategories_',
POLICY_TAGS: 'policyTags_',
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 @@ -486,7 +485,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
55 changes: 39 additions & 16 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 @@ -2078,22 +2069,36 @@ function isReportFieldDisabled(report: OnyxEntry<Report>, reportField: OnyxEntry
/**
* Given a set of report fields, return the field of type formula
*/
function getFormulaTypeReportField(reportFields: PolicyReportFields) {
function getFormulaTypeReportField(reportFields: Record<string, PolicyReportField>) {
return Object.values(reportFields).find((field) => field.type === 'formula');
allroundexperts marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* 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}`;
Copy link
Contributor

Choose a reason for hiding this comment

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

Adding expensify_ prefix to all fieldID cause a problem with the title fieldID text_title which shouldn't have this prefix, and we dealt with in #40464

}

/**
* 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 @@ -2102,7 +2107,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 @@ -2113,15 +2118,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 @@ -5539,6 +5561,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
Loading