Skip to content

Commit 951a1ef

Browse files
authored
Merge pull request #89178 from callstack-internal/refactor/decompose-MoneyRequestConfirmationListFooter
refactor: decompose MoneyRequestConfirmationListFooter into field groups
2 parents 960631a + 300cb23 commit 951a1ef

11 files changed

Lines changed: 1747 additions & 563 deletions

src/components/MoneyRequestConfirmationListFooter.tsx

Lines changed: 133 additions & 563 deletions
Large diffs are not rendered by default.
Lines changed: 385 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,385 @@
1+
import React from 'react';
2+
import {View} from 'react-native';
3+
import type {OnyxEntry} from 'react-native-onyx';
4+
import type {ValueOf} from 'type-fest';
5+
import Button from '@components/Button';
6+
import Icon from '@components/Icon';
7+
import Text from '@components/Text';
8+
import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
9+
import useLocalize from '@hooks/useLocalize';
10+
import useTheme from '@hooks/useTheme';
11+
import useThemeStyles from '@hooks/useThemeStyles';
12+
import variables from '@styles/variables';
13+
import type CONST from '@src/CONST';
14+
import type {IOUAction, IOUType} from '@src/CONST';
15+
import type * as OnyxTypes from '@src/types/onyx';
16+
import type {Participant} from '@src/types/onyx/IOU';
17+
import type {Unit} from '@src/types/onyx/Policy';
18+
import ClassificationFields from './fieldGroups/ClassificationFields';
19+
import computeFieldVisibility, {hasBelowShowMore} from './fieldGroups/fieldVisibility';
20+
import SettingsFields from './fieldGroups/SettingsFields';
21+
import TransactionDetailsFields from './fieldGroups/TransactionDetailsFields';
22+
23+
type TagVisibilityEntry = {
24+
/** Whether this tag list should be displayed */
25+
shouldShow: boolean;
26+
/** Whether the tag for this list is required to submit */
27+
isTagRequired: boolean;
28+
};
29+
30+
type ConfirmationFieldListProps = {
31+
/** Action being performed (drives section navigation targets) */
32+
action: IOUAction;
33+
34+
/** Type of IOU being confirmed */
35+
iouType: Exclude<IOUType, typeof CONST.IOU.TYPE.REQUEST | typeof CONST.IOU.TYPE.SEND>;
36+
37+
/** ID of the active transaction */
38+
transactionID: string | undefined;
39+
40+
/** ID of the report the transaction belongs to */
41+
reportID: string;
42+
43+
/** ID of the originating report action, when editing */
44+
reportActionID: string | undefined;
45+
46+
/** Active transaction */
47+
transaction: OnyxEntry<OnyxTypes.Transaction>;
48+
49+
/** Active policy */
50+
policy: OnyxEntry<OnyxTypes.Policy>;
51+
52+
/** Resolved policy used when moving an expense off track-expense (drives tax fallback) */
53+
policyForMovingExpenses: OnyxEntry<OnyxTypes.Policy> | undefined;
54+
55+
/** Tag lists configured on the policy */
56+
policyTagLists: Array<ValueOf<OnyxTypes.PolicyTagLists>>;
57+
58+
/** Per-tag-list visibility (parallel to `policyTagLists` order) */
59+
tagVisibility: TagVisibilityEntry[];
60+
61+
/** Previous render's per-tag-list `shouldShow` projection (drives `TagFields` transitions) */
62+
previousTagsVisibility: boolean[];
63+
64+
/** Selected participants (drives ReportField presentation) */
65+
selectedParticipants: Participant[];
66+
67+
/** Whether the surface is read-only */
68+
isReadOnly: boolean;
69+
70+
/** Whether the user has confirmed (locks editable controls) */
71+
didConfirm: boolean;
72+
73+
/** Whether the new manual expense flow beta is enabled */
74+
isNewManualExpenseFlowEnabled: boolean;
75+
76+
/** Whether to show smart-scan-driven fields (amount, merchant, date) */
77+
shouldShowSmartScanFields: boolean;
78+
79+
/** Whether the amount field should be displayed when smart-scan fields are shown */
80+
shouldShowAmountField: boolean;
81+
82+
/** Whether the merchant field should be displayed */
83+
shouldShowMerchant: boolean;
84+
85+
/** Whether the categories field should be displayed */
86+
shouldShowCategories: boolean;
87+
88+
/** Whether the date field should be displayed (smart-scan or distance) */
89+
shouldShowDate: boolean;
90+
91+
/** Whether the tax field should be displayed */
92+
shouldShowTax: boolean;
93+
94+
/** Whether the attendees field should be displayed */
95+
shouldShowAttendees: boolean;
96+
97+
/** Whether the time-request fields should be displayed */
98+
shouldShowTimeRequestFields: boolean;
99+
100+
/** Whether the billable toggle should be displayed */
101+
shouldShowBillable: boolean;
102+
103+
/** Whether the reimbursable toggle should be displayed */
104+
shouldShowReimbursable: boolean;
105+
106+
/** Whether navigating to upgrade is required to proceed past blocked workspaces */
107+
shouldNavigateToUpgradePath: boolean;
108+
109+
/** Whether the user must select a policy before submitting */
110+
shouldSelectPolicy: boolean;
111+
112+
/** Whether tax field modifications are allowed */
113+
canModifyTaxFields: boolean;
114+
115+
/** Whether the active transaction is a distance request */
116+
isDistanceRequest: boolean;
117+
118+
/** Whether the active transaction is a manual distance request */
119+
isManualDistanceRequest: boolean;
120+
121+
/** Whether the active transaction is an odometer distance request */
122+
isOdometerDistanceRequest: boolean;
123+
124+
/** Whether the active transaction is a GPS distance request */
125+
isGPSDistanceRequest: boolean;
126+
127+
/** Whether the merchant is required to submit */
128+
isMerchantRequired: boolean | undefined;
129+
130+
/** Whether the description is required to submit */
131+
isDescriptionRequired: boolean;
132+
133+
/** Whether the categories field is required */
134+
isCategoryRequired: boolean;
135+
136+
/** Whether the surface is in a policy-expense chat */
137+
isPolicyExpenseChat: boolean;
138+
139+
/** Whether we're editing an existing split expense */
140+
isEditingSplitBill: boolean;
141+
142+
/** Whether the active transaction is a per-diem request */
143+
isPerDiemRequest: boolean;
144+
145+
/** Whether to display per-field validation errors */
146+
shouldDisplayFieldError: boolean;
147+
148+
/** Form-level error message */
149+
formError: string;
150+
151+
/** ISO currency code for the transaction */
152+
iouCurrencyCode: string;
153+
154+
/** Total amount, in the smallest currency unit */
155+
amount: number;
156+
157+
/** Pre-formatted amount string for display */
158+
formattedAmount: string;
159+
160+
/** Pre-formatted amount-per-attendee string for display */
161+
formattedAmountPerAttendee: string;
162+
163+
/** Distance value (drives `DistanceField`) */
164+
distance: number;
165+
166+
/** Whether a route is available for distance requests */
167+
hasRoute: boolean;
168+
169+
/** Distance unit */
170+
unit: Unit | undefined;
171+
172+
/** Distance rate (per-unit cost) */
173+
rate: number | undefined;
174+
175+
/** Display name of the active distance rate */
176+
distanceRateName: string | undefined;
177+
178+
/** Currency of the active distance rate */
179+
distanceRateCurrency: string;
180+
181+
/** Callback when reimbursable is toggled */
182+
onToggleReimbursable?: (isOn: boolean) => void;
183+
184+
/** Callback when billable is toggled */
185+
onToggleBillable?: (isOn: boolean) => void;
186+
187+
/** Setter that expands the optional fields when the user taps "Show more" */
188+
setShowMoreFields: (showMoreFields: boolean) => void;
189+
190+
/** Whether the receipt area is using compact mode (drives the show-more split) */
191+
isCompactMode: boolean;
192+
};
193+
194+
function ConfirmationFieldList({
195+
action,
196+
iouType,
197+
transactionID,
198+
reportID,
199+
reportActionID,
200+
transaction,
201+
policy,
202+
policyForMovingExpenses,
203+
policyTagLists,
204+
tagVisibility,
205+
previousTagsVisibility,
206+
selectedParticipants,
207+
isReadOnly,
208+
didConfirm,
209+
isNewManualExpenseFlowEnabled,
210+
shouldShowSmartScanFields,
211+
shouldShowAmountField,
212+
shouldShowMerchant,
213+
shouldShowCategories,
214+
shouldShowDate,
215+
shouldShowTax,
216+
shouldShowAttendees,
217+
shouldShowTimeRequestFields,
218+
shouldShowBillable,
219+
shouldShowReimbursable,
220+
shouldNavigateToUpgradePath,
221+
shouldSelectPolicy,
222+
canModifyTaxFields,
223+
isDistanceRequest,
224+
isManualDistanceRequest,
225+
isOdometerDistanceRequest,
226+
isGPSDistanceRequest,
227+
isMerchantRequired,
228+
isDescriptionRequired,
229+
isCategoryRequired,
230+
isPolicyExpenseChat,
231+
isEditingSplitBill,
232+
isPerDiemRequest,
233+
shouldDisplayFieldError,
234+
formError,
235+
iouCurrencyCode,
236+
amount,
237+
formattedAmount,
238+
formattedAmountPerAttendee,
239+
distance,
240+
hasRoute,
241+
unit,
242+
rate,
243+
distanceRateName,
244+
distanceRateCurrency,
245+
onToggleReimbursable,
246+
onToggleBillable,
247+
setShowMoreFields,
248+
isCompactMode,
249+
}: ConfirmationFieldListProps) {
250+
const styles = useThemeStyles();
251+
const theme = useTheme();
252+
const {translate} = useLocalize();
253+
const icons = useMemoizedLazyExpensifyIcons(['Sparkles', 'DownArrow']);
254+
255+
const fieldVisibility = computeFieldVisibility({
256+
shouldShowSmartScanFields,
257+
shouldShowAmountField,
258+
isDistanceRequest,
259+
shouldShowMerchant,
260+
shouldShowTimeRequestFields,
261+
shouldShowCategories,
262+
isCategoryRequired,
263+
shouldShowDate,
264+
tagVisibility,
265+
policyTagLists,
266+
shouldShowTax,
267+
shouldShowAttendees,
268+
shouldShowReimbursable,
269+
shouldShowBillable,
270+
isPolicyExpenseChat,
271+
});
272+
const shouldShowMoreButton = hasBelowShowMore(fieldVisibility);
273+
274+
return (
275+
<View style={[styles.mb5, styles.mt2]}>
276+
{isCompactMode && (
277+
<View style={[styles.flexRow, styles.alignItemsCenter, styles.pl5, styles.gap2, styles.mb2, styles.pr10]}>
278+
<Icon
279+
src={icons.Sparkles}
280+
fill={theme.icon}
281+
width={variables.iconSizeNormal}
282+
height={variables.iconSizeNormal}
283+
/>
284+
<Text style={styles.rightLabelMenuItem}>{translate('iou.automaticallyEnterExpenseDetails')}</Text>
285+
</View>
286+
)}
287+
288+
<TransactionDetailsFields
289+
action={action}
290+
iouType={iouType}
291+
transactionID={transactionID}
292+
reportID={reportID}
293+
reportActionID={reportActionID}
294+
transaction={transaction}
295+
policy={policy}
296+
isReadOnly={isReadOnly}
297+
didConfirm={didConfirm}
298+
isNewManualExpenseFlowEnabled={isNewManualExpenseFlowEnabled}
299+
isEditingSplitBill={isEditingSplitBill}
300+
isPolicyExpenseChat={isPolicyExpenseChat}
301+
isManualDistanceRequest={isManualDistanceRequest}
302+
isOdometerDistanceRequest={isOdometerDistanceRequest}
303+
isGPSDistanceRequest={isGPSDistanceRequest}
304+
isMerchantRequired={isMerchantRequired}
305+
isDescriptionRequired={isDescriptionRequired}
306+
shouldDisplayFieldError={shouldDisplayFieldError}
307+
formError={formError}
308+
shouldNavigateToUpgradePath={shouldNavigateToUpgradePath}
309+
shouldSelectPolicy={shouldSelectPolicy}
310+
iouCurrencyCode={iouCurrencyCode}
311+
amount={amount}
312+
formattedAmount={formattedAmount}
313+
distance={distance}
314+
hasRoute={hasRoute}
315+
unit={unit}
316+
rate={rate}
317+
distanceRateName={distanceRateName}
318+
distanceRateCurrency={distanceRateCurrency}
319+
isCompactMode={isCompactMode}
320+
fieldVisibility={fieldVisibility}
321+
/>
322+
323+
<ClassificationFields
324+
action={action}
325+
iouType={iouType}
326+
transactionID={transactionID}
327+
reportID={reportID}
328+
reportActionID={reportActionID}
329+
transaction={transaction}
330+
policy={policy}
331+
policyForMovingExpenses={policyForMovingExpenses}
332+
policyTagLists={policyTagLists}
333+
previousTagsVisibility={previousTagsVisibility}
334+
isReadOnly={isReadOnly}
335+
didConfirm={didConfirm}
336+
isCategoryRequired={isCategoryRequired}
337+
canModifyTaxFields={canModifyTaxFields}
338+
shouldDisplayFieldError={shouldDisplayFieldError}
339+
shouldNavigateToUpgradePath={shouldNavigateToUpgradePath}
340+
shouldSelectPolicy={shouldSelectPolicy}
341+
iouCurrencyCode={iouCurrencyCode}
342+
formattedAmountPerAttendee={formattedAmountPerAttendee}
343+
formError={formError}
344+
isCompactMode={isCompactMode}
345+
fieldVisibility={fieldVisibility}
346+
/>
347+
348+
<SettingsFields
349+
action={action}
350+
iouType={iouType}
351+
transactionID={transactionID}
352+
reportID={reportID}
353+
reportActionID={reportActionID}
354+
transaction={transaction}
355+
selectedParticipants={selectedParticipants}
356+
isReadOnly={isReadOnly}
357+
shouldShowBillable={shouldShowBillable}
358+
shouldShowReimbursable={shouldShowReimbursable}
359+
isPolicyExpenseChat={isPolicyExpenseChat}
360+
isPerDiemRequest={isPerDiemRequest}
361+
onToggleReimbursable={onToggleReimbursable}
362+
onToggleBillable={onToggleBillable}
363+
isCompactMode={isCompactMode}
364+
fieldVisibility={fieldVisibility}
365+
/>
366+
367+
{isCompactMode && shouldShowMoreButton && (
368+
<View style={[styles.mt3, styles.alignItemsCenter, styles.pRelative, styles.mh5]}>
369+
<View style={[styles.dividerLine, styles.pAbsolute, styles.w100, styles.justifyContentCenter, {transform: [{translateY: -0.5}]}]} />
370+
<Button
371+
text={translate('common.showMore')}
372+
onPress={() => setShowMoreFields(true)}
373+
small
374+
shouldShowRightIcon
375+
iconRight={icons.DownArrow}
376+
innerStyles={[styles.hoveredComponentBG, styles.ph4, styles.pv2]}
377+
textStyles={styles.buttonSmallText}
378+
/>
379+
</View>
380+
)}
381+
</View>
382+
);
383+
}
384+
385+
export default ConfirmationFieldList;

0 commit comments

Comments
 (0)