Skip to content

Commit 0e39e56

Browse files
committed
refactor: decompose MoneyRequestConfirmationListFooter into field groups
1 parent c71784d commit 0e39e56

10 files changed

Lines changed: 1629 additions & 565 deletions

src/components/MoneyRequestConfirmationListFooter.tsx

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

0 commit comments

Comments
 (0)