diff --git a/src/components/AddPaymentCard/PaymentCardChangeCurrencyForm.tsx b/src/components/AddPaymentCard/PaymentCardChangeCurrencyForm.tsx index 8ccee64f1118..22f32d7f50d3 100644 --- a/src/components/AddPaymentCard/PaymentCardChangeCurrencyForm.tsx +++ b/src/components/AddPaymentCard/PaymentCardChangeCurrencyForm.tsx @@ -84,6 +84,7 @@ function PaymentCardChangeCurrencyForm({changeBillingCurrency, isSecurityCodeReq submitButtonText={translate('common.save')} scrollContextEnabled style={[styles.mh5, styles.flexGrow1]} + shouldHideFixErrorsAlert > <> diff --git a/src/components/SubStepForms/DateOfBirthStep.tsx b/src/components/SubStepForms/DateOfBirthStep.tsx index 3142b46277f1..54f1510800b4 100644 --- a/src/components/SubStepForms/DateOfBirthStep.tsx +++ b/src/components/SubStepForms/DateOfBirthStep.tsx @@ -84,6 +84,7 @@ function DateOfBirthStep({ style={[styles.mh5, styles.flexGrow2, styles.justifyContentBetween]} submitButtonStyles={[styles.mb0]} enabledWhenOffline + shouldHideFixErrorsAlert > {formTitle} ({ style={[styles.mh5, styles.flexGrow1]} submitButtonStyles={[styles.mb0]} enabledWhenOffline={enabledWhenOffline} + shouldHideFixErrorsAlert > {formTitle} diff --git a/src/components/SubStepForms/YesNoStep.tsx b/src/components/SubStepForms/YesNoStep.tsx index 50e26cef78f3..45181b5f875e 100644 --- a/src/components/SubStepForms/YesNoStep.tsx +++ b/src/components/SubStepForms/YesNoStep.tsx @@ -58,6 +58,7 @@ function YesNoStep({title, description, defaultValue, onSelectedValue, submitBut style={[styles.mh5, styles.flexGrow1]} submitButtonStyles={submitButtonStyles} isLoading={reimbursementAccount?.isSavingCorpayOnboardingBeneficialOwnersFields} + shouldHideFixErrorsAlert > {title} {description} diff --git a/src/components/TextPicker/TextSelectorModal.tsx b/src/components/TextPicker/TextSelectorModal.tsx index c0c733956e86..58886c84fa29 100644 --- a/src/components/TextPicker/TextSelectorModal.tsx +++ b/src/components/TextPicker/TextSelectorModal.tsx @@ -123,6 +123,7 @@ function TextSelectorModal({ submitButtonText={translate('common.save')} style={[styles.mh5, styles.flex1]} enabledWhenOffline + shouldHideFixErrorsAlert > {!!subtitle && {subtitle}} ; - - /** Contains plaid data */ - plaidData: OnyxEntry; -}; - -type PlaidStepProps = PlaidOnyxProps & SubStepProps; const BANK_INFO_STEP_KEYS = INPUT_IDS.BANK_INFO_STEP; -function PlaidStep({personalBankAccountDraft, onNext, plaidData}: PlaidStepProps) { +function PlaidStep({onNext}: SubStepProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); const isFocused = useIsFocused(); + const [personalBankAccountDraft] = useOnyx(ONYXKEYS.FORMS.PERSONAL_BANK_ACCOUNT_FORM_DRAFT); + const [plaidData] = useOnyx(ONYXKEYS.PLAID_DATA); const selectedPlaidAccountID = personalBankAccountDraft?.[BANK_INFO_STEP_KEYS.PLAID_ACCOUNT_ID] ?? ''; const handleNextPress = useCallback(() => { @@ -47,12 +36,12 @@ function PlaidStep({personalBankAccountDraft, onNext, plaidData}: PlaidStepProps [BANK_INFO_STEP_KEYS.PLAID_ACCESS_TOKEN]: plaidData?.[BANK_INFO_STEP_KEYS.PLAID_ACCESS_TOKEN] ?? '', }; - BankAccounts.updateAddPersonalBankAccountDraft(bankAccountData); + updateAddPersonalBankAccountDraft(bankAccountData); onNext(); }, [onNext, personalBankAccountDraft, plaidData]); const handleSelectPlaidAccount = (plaidAccountID: string) => { - BankAccounts.updateAddPersonalBankAccountDraft({plaidAccountID}); + updateAddPersonalBankAccountDraft({plaidAccountID}); }; useEffect(() => { @@ -60,25 +49,26 @@ function PlaidStep({personalBankAccountDraft, onNext, plaidData}: PlaidStepProps if (isFocused || plaidBankAccounts.length) { return; } - BankAccounts.clearPersonalBankAccountSetupType(); + clearPersonalBankAccountSetupType(); }, [isFocused, plaidData]); return ( 0} + shouldHideFixErrorsAlert > ({ - personalBankAccountDraft: { - key: ONYXKEYS.FORMS.PERSONAL_BANK_ACCOUNT_FORM_DRAFT, - }, - plaidData: { - key: ONYXKEYS.PLAID_DATA, - }, -})(PlaidStep); +export default PlaidStep; diff --git a/src/pages/GroupChatNameEditPage.tsx b/src/pages/GroupChatNameEditPage.tsx index a67f1fd0f3e2..5ca9576d165b 100644 --- a/src/pages/GroupChatNameEditPage.tsx +++ b/src/pages/GroupChatNameEditPage.tsx @@ -91,6 +91,7 @@ function GroupChatNameEditPage({report}: GroupChatNameEditPageProps) { validate={validate} style={[styles.mh5, styles.flex1]} enabledWhenOffline + shouldHideFixErrorsAlert > {translate( diff --git a/src/pages/ReimbursementAccount/NonUSD/BankInfo/subSteps/AccountHolderDetails.tsx b/src/pages/ReimbursementAccount/NonUSD/BankInfo/subSteps/AccountHolderDetails.tsx index 9ce9e9862e81..47f47b778e4d 100644 --- a/src/pages/ReimbursementAccount/NonUSD/BankInfo/subSteps/AccountHolderDetails.tsx +++ b/src/pages/ReimbursementAccount/NonUSD/BankInfo/subSteps/AccountHolderDetails.tsx @@ -123,6 +123,7 @@ function AccountHolderDetails({onNext, isEditing, corpayFields}: BankInfoSubStep validate={validate} onSubmit={handleSubmit} style={[styles.mh5, styles.flexGrow1]} + shouldHideFixErrorsAlert={(accountHolderDetailsFields?.length ?? 0) <= 1} > {translate('bankInfoStep.whatAreYour')} diff --git a/src/pages/ReimbursementAccount/NonUSD/BankInfo/subSteps/BankAccountDetails.tsx b/src/pages/ReimbursementAccount/NonUSD/BankInfo/subSteps/BankAccountDetails.tsx index 000e18bb37d0..2a0971d3617c 100644 --- a/src/pages/ReimbursementAccount/NonUSD/BankInfo/subSteps/BankAccountDetails.tsx +++ b/src/pages/ReimbursementAccount/NonUSD/BankInfo/subSteps/BankAccountDetails.tsx @@ -100,6 +100,7 @@ function BankAccountDetails({onNext, isEditing, corpayFields}: BankInfoSubStepPr validate={validate} style={[styles.mh5, styles.flexGrow1]} isSubmitDisabled={!inputs} + shouldHideFixErrorsAlert={(bankAccountDetailsFields?.length ?? 0) <= 1} > <> {translate('bankInfoStep.whatAreYour')} diff --git a/src/pages/ReimbursementAccount/NonUSD/BeneficialOwnerInfo/BeneficialOwnerDetailsFormSubSteps/Documents.tsx b/src/pages/ReimbursementAccount/NonUSD/BeneficialOwnerInfo/BeneficialOwnerDetailsFormSubSteps/Documents.tsx index d614554557e9..be819b6f863d 100644 --- a/src/pages/ReimbursementAccount/NonUSD/BeneficialOwnerInfo/BeneficialOwnerDetailsFormSubSteps/Documents.tsx +++ b/src/pages/ReimbursementAccount/NonUSD/BeneficialOwnerInfo/BeneficialOwnerDetailsFormSubSteps/Documents.tsx @@ -87,6 +87,14 @@ function Documents({onNext, isEditing, ownerBeingModifiedID}: DocumentsProps) { shouldSaveDraft: isEditing, }); + const testForShouldHideFixErrorsAlert = + [ + isDocumentNeededStatus.isProofOfOwnershipNeeded, + isDocumentNeededStatus.isCopyOfIDNeeded, + isDocumentNeededStatus.isProofOfAddressNeeded, + isDocumentNeededStatus.isCodiceFiscaleNeeded, + ].filter(Boolean).length <= 1; + return ( {translate('ownershipInfoStep.uploadDocuments')} {translate('ownershipInfoStep.pleaseUpload')} diff --git a/src/pages/ReimbursementAccount/NonUSD/BusinessInfo/subSteps/AverageReimbursement.tsx b/src/pages/ReimbursementAccount/NonUSD/BusinessInfo/subSteps/AverageReimbursement.tsx index ddf7bcfc9241..22b1c33aa31e 100644 --- a/src/pages/ReimbursementAccount/NonUSD/BusinessInfo/subSteps/AverageReimbursement.tsx +++ b/src/pages/ReimbursementAccount/NonUSD/BusinessInfo/subSteps/AverageReimbursement.tsx @@ -58,6 +58,7 @@ function AverageReimbursement({onNext, isEditing}: AverageReimbursementProps) { validate={validate} style={[styles.flexGrow1]} submitButtonStyles={[styles.mh5]} + shouldHideFixErrorsAlert > {translate('businessInfoStep.whatsYourExpectedAverageReimbursements')} {translate('businessInfoStep.whereWasTheBusinessIncorporated')} {shouldGatherState && ( diff --git a/src/pages/ReimbursementAccount/NonUSD/BusinessInfo/subSteps/PaymentVolume.tsx b/src/pages/ReimbursementAccount/NonUSD/BusinessInfo/subSteps/PaymentVolume.tsx index 70d610687212..5a1758452c10 100644 --- a/src/pages/ReimbursementAccount/NonUSD/BusinessInfo/subSteps/PaymentVolume.tsx +++ b/src/pages/ReimbursementAccount/NonUSD/BusinessInfo/subSteps/PaymentVolume.tsx @@ -58,6 +58,7 @@ function PaymentVolume({onNext, isEditing}: PaymentVolumeProps) { validate={validate} style={[styles.flexGrow1]} submitButtonStyles={[styles.mh5]} + shouldHideFixErrorsAlert > {translate('businessInfoStep.whatsTheBusinessAnnualPayment')} {translate('businessInfoStep.whatsTheBusinessRegistrationNumber')} ): FormInputErrors => { - const errors = ValidationUtils.getFieldRequiredErrors(values, shouldGatherBothEmails ? [SIGNER_EMAIL, SECOND_SIGNER_EMAIL] : [SIGNER_EMAIL]); + const errors = getFieldRequiredErrors(values, shouldGatherBothEmails ? [SIGNER_EMAIL, SECOND_SIGNER_EMAIL] : [SIGNER_EMAIL]); if (values[SIGNER_EMAIL] && !Str.isValidEmail(values[SIGNER_EMAIL])) { errors[SIGNER_EMAIL] = translate('bankAccount.error.email'); } @@ -56,6 +57,7 @@ function EnterEmail({onSubmit, isUserDirector}: EnterEmailProps) { onSubmit={onSubmit} validate={validate} style={[styles.mh5, styles.flexGrow1]} + shouldHideFixErrorsAlert={!shouldGatherBothEmails} > {translate(shouldGatherBothEmails ? 'signerInfoStep.enterTwoEmails' : 'signerInfoStep.enterOneEmail')} {!shouldGatherBothEmails && {translate('signerInfoStep.regulationRequiresOneMoreDirector')}} diff --git a/src/pages/ReimbursementAccount/NonUSD/SignerInfo/subSteps/UploadDocuments.tsx b/src/pages/ReimbursementAccount/NonUSD/SignerInfo/subSteps/UploadDocuments.tsx index 449d5a74a9d2..bd83f80112b6 100644 --- a/src/pages/ReimbursementAccount/NonUSD/SignerInfo/subSteps/UploadDocuments.tsx +++ b/src/pages/ReimbursementAccount/NonUSD/SignerInfo/subSteps/UploadDocuments.tsx @@ -83,6 +83,7 @@ function UploadDocuments({onNext, isEditing}: UploadDocumentsProps) { onSubmit={handleSubmit} validate={validate} style={[styles.mh5, styles.flex1]} + shouldHideFixErrorsAlert > {translate('signerInfoStep.uploadID')} diff --git a/src/pages/ReimbursementAccount/USD/BankInfo/subSteps/Plaid.tsx b/src/pages/ReimbursementAccount/USD/BankInfo/subSteps/Plaid.tsx index 99b1fdd02a85..64f415b98f2f 100644 --- a/src/pages/ReimbursementAccount/USD/BankInfo/subSteps/Plaid.tsx +++ b/src/pages/ReimbursementAccount/USD/BankInfo/subSteps/Plaid.tsx @@ -9,6 +9,7 @@ import type {SubStepProps} from '@hooks/useSubStep/types'; import useThemeStyles from '@hooks/useThemeStyles'; import {setBankAccountSubStep, validatePlaidSelection} from '@userActions/BankAccounts'; import {updateReimbursementAccountDraft} from '@userActions/ReimbursementAccount'; +import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import INPUT_IDS from '@src/types/form/ReimbursementAccountForm'; @@ -47,7 +48,7 @@ function Plaid({onNext, setUSDBankAccountStep}: PlaidProps) { onNext(bankAccountData); }, [plaidData, reimbursementAccountDraft, onNext]); - const bankAccountID = Number(reimbursementAccount?.achData?.bankAccountID ?? '-1'); + const bankAccountID = reimbursementAccount?.achData?.bankAccountID ?? CONST.DEFAULT_NUMBER_ID; useEffect(() => { const plaidBankAccounts = plaidData?.bankAccounts ?? []; @@ -72,6 +73,7 @@ function Plaid({onNext, setUSDBankAccountStep}: PlaidProps) { submitButtonText={translate('common.next')} style={[styles.mh5, styles.flexGrow1]} isSubmitButtonVisible={(plaidData?.bankAccounts ?? []).length > 0} + shouldHideFixErrorsAlert > ): FormInputErrors => { - const errors = ValidationUtils.getFieldRequiredErrors(values, [BUSINESS_INFO_STEP_KEYS.HAS_NO_CONNECTION_TO_CANNABIS]); + const errors = getFieldRequiredErrors(values, [BUSINESS_INFO_STEP_KEYS.HAS_NO_CONNECTION_TO_CANNABIS]); if (!values.hasNoConnectionToCannabis) { errors.hasNoConnectionToCannabis = translate('bankAccount.error.restrictedBusiness'); @@ -136,6 +136,7 @@ function ConfirmationBusiness({onNext, onMove}: SubStepProps) { submitButtonText={translate('common.confirm')} style={[styles.mh5, styles.flexGrow1]} enabledWhenOffline={false} + shouldHideFixErrorsAlert > ): FormInputErrors => { - const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); + const errors = getFieldRequiredErrors(values, STEP_FIELDS); - if (values.incorporationDate && !ValidationUtils.isValidDate(values.incorporationDate)) { + if (values.incorporationDate && !isValidDate(values.incorporationDate)) { errors.incorporationDate = translate('common.error.dateInvalid'); - } else if (values.incorporationDate && !ValidationUtils.isValidPastDate(values.incorporationDate)) { + } else if (values.incorporationDate && !isValidPastDate(values.incorporationDate)) { errors.incorporationDate = translate('bankAccount.error.incorporationDateFuture'); } @@ -54,6 +54,7 @@ function IncorporationDateBusiness({onNext, isEditing}: SubStepProps) { onSubmit={handleSubmit} style={[styles.mh5, styles.flexGrow1]} submitButtonStyles={[styles.mb0]} + shouldHideFixErrorsAlert > {translate('businessInfoStep.selectYourCompanysIncorporationDate')} ): FormInputErrors => - ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); + getFieldRequiredErrors(values, STEP_FIELDS); function IncorporationStateBusiness({onNext, isEditing}: SubStepProps) { const {translate} = useLocalize(); @@ -41,6 +41,7 @@ function IncorporationStateBusiness({onNext, isEditing}: SubStepProps) { onSubmit={handleSubmit} style={[styles.mh0, styles.flexGrow1]} submitButtonStyles={[styles.ph5, styles.mb0]} + shouldHideFixErrorsAlert > {translate('businessInfoStep.pleaseSelectTheStateYourCompanyWasIncorporatedIn')} ): FormInputErrors => { - const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); + const errors = getFieldRequiredErrors(values, STEP_FIELDS); - if (values.companyName && !ValidationUtils.isValidCompanyName(values.companyName)) { + if (values.companyName && !isValidCompanyName(values.companyName)) { errors.companyName = translate('bankAccount.error.companyName'); } @@ -55,6 +55,7 @@ function NameBusiness({onNext, isEditing}: SubStepProps) { onSubmit={handleSubmit} style={[styles.mh5, styles.flexGrow1]} submitButtonStyles={[styles.mb0]} + shouldHideFixErrorsAlert > {translate('businessInfoStep.enterTheNameOfYourBusiness')} ): FormInputErrors => { - const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); + const errors = getFieldRequiredErrors(values, STEP_FIELDS); - if (values.companyPhone && !ValidationUtils.isValidUSPhone(values.companyPhone, true)) { + if (values.companyPhone && !isValidUSPhone(values.companyPhone, true)) { errors.companyPhone = translate('bankAccount.error.phoneNumber'); } @@ -53,6 +53,7 @@ function PhoneNumberBusiness({onNext, isEditing}: SubStepProps) { onSubmit={handleSubmit} style={[styles.mh5, styles.flexGrow1]} submitButtonStyles={[styles.mb0]} + shouldHideFixErrorsAlert > {translate('businessInfoStep.enterYourCompanysPhoneNumber')} ): FormInputErrors => { - const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); + const errors = getFieldRequiredErrors(values, STEP_FIELDS); - if (values.companyTaxID && !ValidationUtils.isValidTaxID(values.companyTaxID)) { + if (values.companyTaxID && !isValidTaxID(values.companyTaxID)) { errors.companyTaxID = translate('bankAccount.error.taxID'); } @@ -53,6 +53,7 @@ function TaxIdBusiness({onNext, isEditing}: SubStepProps) { onSubmit={handleSubmit} style={[styles.mh5, styles.flexGrow1]} submitButtonStyles={[styles.mb0]} + shouldHideFixErrorsAlert > {translate('businessInfoStep.enterYourCompanysTaxIdNumber')} ): FormInputErrors => - ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); + getFieldRequiredErrors(values, STEP_FIELDS); const defaultIncorporationType = reimbursementAccount?.achData?.incorporationType ?? ''; @@ -41,6 +41,7 @@ function TypeBusiness({onNext, isEditing}: SubStepProps) { onSubmit={handleSubmit} style={[styles.flexGrow1]} submitButtonStyles={[styles.ph5, styles.mb0]} + shouldHideFixErrorsAlert > {translate('businessInfoStep.selectYourCompanysType')} ): FormInputErrors => { - const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); + const errors = getFieldRequiredErrors(values, STEP_FIELDS); - if (values.website && !ValidationUtils.isValidWebsite(Str.sanitizeURL(values.website, CONST.COMPANY_WEBSITE_DEFAULT_SCHEME))) { + if (values.website && !isValidWebsite(Str.sanitizeURL(values.website, CONST.COMPANY_WEBSITE_DEFAULT_SCHEME))) { errors.website = translate('bankAccount.error.website'); } @@ -46,7 +46,7 @@ function WebsiteBusiness({onNext, isEditing}: SubStepProps) { fieldIds: STEP_FIELDS, onNext: (values) => { const website = Str.sanitizeURL((values as {website: string})?.website, CONST.COMPANY_WEBSITE_DEFAULT_SCHEME); - BankAccounts.addBusinessWebsiteForDraft(website); + addBusinessWebsiteForDraft(website); onNext(); }, shouldSaveDraft: true, @@ -60,6 +60,7 @@ function WebsiteBusiness({onNext, isEditing}: SubStepProps) { onSubmit={handleSubmit} style={[styles.mh5, styles.flexGrow1]} submitButtonStyles={[styles.mb0]} + shouldHideFixErrorsAlert > {translate('businessInfoStep.enterYourCompanysWebsite')} {translate('common.websiteExample')} diff --git a/src/pages/RoomDescriptionPage.tsx b/src/pages/RoomDescriptionPage.tsx index 799ca14dcf95..14885c2ba08c 100644 --- a/src/pages/RoomDescriptionPage.tsx +++ b/src/pages/RoomDescriptionPage.tsx @@ -109,6 +109,7 @@ function RoomDescriptionPage({report, policies}: RoomDescriptionPageProps) { validate={validate} submitButtonText={translate('common.save')} enabledWhenOffline + shouldHideFixErrorsAlert > {translate('reportDescriptionPage.explainerText')} diff --git a/src/pages/Search/SavedSearchRenamePage.tsx b/src/pages/Search/SavedSearchRenamePage.tsx index 695949661fb1..6e8444bbefbb 100644 --- a/src/pages/Search/SavedSearchRenamePage.tsx +++ b/src/pages/Search/SavedSearchRenamePage.tsx @@ -61,6 +61,7 @@ function SavedSearchRenamePage({route}: {route: {params: {q: string; name: strin onSubmit={onSaveSearch} style={[styles.mh5, styles.flex1]} enabledWhenOffline + shouldHideFixErrorsAlert > {translate('iou.explainHold')} diff --git a/src/pages/iou/request/step/IOURequestStepDate.tsx b/src/pages/iou/request/step/IOURequestStepDate.tsx index 4536c43c9ae4..303b1b453c0b 100644 --- a/src/pages/iou/request/step/IOURequestStepDate.tsx +++ b/src/pages/iou/request/step/IOURequestStepDate.tsx @@ -1,83 +1,71 @@ import lodashIsEmpty from 'lodash/isEmpty'; -import React from 'react'; +import React, {useMemo} from 'react'; import type {OnyxEntry} from 'react-native-onyx'; -import {withOnyx} from 'react-native-onyx'; +import {useOnyx} from 'react-native-onyx'; import DatePicker from '@components/DatePicker'; import FormProvider from '@components/Form/FormProvider'; import InputWrapper from '@components/Form/InputWrapper'; import type {FormOnyxValues} from '@components/Form/types'; import useLocalize from '@hooks/useLocalize'; +import usePolicy from '@hooks/usePolicy'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as IOUUtils from '@libs/IOUUtils'; +import {isValidMoneyRequestType, shouldUseTransactionDraft} from '@libs/IOUUtils'; import Navigation from '@libs/Navigation/Navigation'; -import * as ReportUtils from '@libs/ReportUtils'; -import * as TransactionUtils from '@libs/TransactionUtils'; -import * as IOU from '@userActions/IOU'; +import {canEditFieldOfMoneyRequest} from '@libs/ReportUtils'; +import {areRequiredFieldsEmpty, getFormattedCreated} from '@libs/TransactionUtils'; +import {setDraftSplitTransaction, setMoneyRequestCreated, updateMoneyRequestDate} from '@userActions/IOU'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; import INPUT_IDS from '@src/types/form/MoneyRequestDateForm'; -import type * as OnyxTypes from '@src/types/onyx'; +import type {Report, Transaction} from '@src/types/onyx'; import StepScreenWrapper from './StepScreenWrapper'; import withFullTransactionOrNotFound from './withFullTransactionOrNotFound'; import type {WithWritableReportOrNotFoundProps} from './withWritableReportOrNotFound'; import withWritableReportOrNotFound from './withWritableReportOrNotFound'; -type IOURequestStepDateOnyxProps = { - /** The draft transaction that holds data to be persisted on the current transaction */ - splitDraftTransaction: OnyxEntry; +type IOURequestStepDateProps = WithWritableReportOrNotFoundProps & { + /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ + transaction: OnyxEntry; - /** The actions from the parent report */ - reportActions: OnyxEntry; - - /** Session info for the currently logged in user. */ - session: OnyxEntry; - - /** The policy of the report */ - policy: OnyxEntry; - - /** Collection of categories attached to a policy */ - policyCategories: OnyxEntry; - - /** Collection of tags attached to a policy */ - policyTags: OnyxEntry; + /** The report linked to the transaction */ + report: OnyxEntry; }; -type IOURequestStepDateProps = IOURequestStepDateOnyxProps & - WithWritableReportOrNotFoundProps & { - /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ - transaction: OnyxEntry; - - /** The report linked to the transaction */ - report: OnyxEntry; - }; - function IOURequestStepDate({ route: { - params: {action, iouType, reportID, backTo, reportActionID}, + params: {action, iouType, reportID, backTo, reportActionID, transactionID}, }, transaction, - splitDraftTransaction, - policy, - policyTags, - policyCategories, - reportActions, report, - session, }: IOURequestStepDateProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); + const policy = usePolicy(report?.policyID); + const [policyCategories] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${report?.policyID}`); + const [policyTags] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${report?.policyID}`); + const [splitDraftTransaction] = useOnyx(`${ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT}${transactionID}`); + const reportActionsReportID = useMemo(() => { + let actionsReportID; + if (action === CONST.IOU.ACTION.EDIT) { + actionsReportID = iouType === CONST.IOU.TYPE.SPLIT ? report?.reportID : report?.parentReportID; + } + return actionsReportID; + }, [action, iouType, report?.reportID, report?.parentReportID]); + const [reportAction] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportActionsReportID}`, { + canEvict: false, + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + selector: (reportActions) => reportActions?.[`${report?.parentReportActionID || reportActionID}`], + }); + const [session] = useOnyx(ONYXKEYS.SESSION); const isEditing = action === CONST.IOU.ACTION.EDIT; // In the split flow, when editing we use SPLIT_TRANSACTION_DRAFT to save draft value const isEditingSplitBill = iouType === CONST.IOU.TYPE.SPLIT && isEditing; - const currentCreated = - isEditingSplitBill && !lodashIsEmpty(splitDraftTransaction) ? TransactionUtils.getFormattedCreated(splitDraftTransaction) : TransactionUtils.getFormattedCreated(transaction); - const parentReportAction = reportActions?.[(isEditingSplitBill ? reportActionID : report?.parentReportActionID) ?? -1]; - const canEditingSplitBill = - isEditingSplitBill && session && parentReportAction && session.accountID === parentReportAction.actorAccountID && TransactionUtils.areRequiredFieldsEmpty(transaction); - const canEditMoneyRequest = isEditing && ReportUtils.canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.DATE); + const currentCreated = isEditingSplitBill && !lodashIsEmpty(splitDraftTransaction) ? getFormattedCreated(splitDraftTransaction) : getFormattedCreated(transaction); + const canEditingSplitBill = isEditingSplitBill && session && reportAction && session.accountID === reportAction.actorAccountID && areRequiredFieldsEmpty(transaction); + const canEditMoneyRequest = isEditing && canEditFieldOfMoneyRequest(reportAction, CONST.EDIT_REQUEST_FIELD.DATE); // eslint-disable-next-line rulesdir/no-negated-variables - const shouldShowNotFound = !IOUUtils.isValidMoneyRequestType(iouType) || (isEditing && !canEditMoneyRequest && !canEditingSplitBill); + const shouldShowNotFound = !isValidMoneyRequestType(iouType) || (isEditing && !canEditMoneyRequest && !canEditingSplitBill); const navigateBack = () => { Navigation.goBack(backTo); @@ -94,17 +82,17 @@ function IOURequestStepDate({ // In the split flow, when editing we use SPLIT_TRANSACTION_DRAFT to save draft value if (isEditingSplitBill) { - IOU.setDraftSplitTransaction(transaction?.transactionID ?? '-1', {created: newCreated}); + setDraftSplitTransaction(transactionID, {created: newCreated}); navigateBack(); return; } - const isTransactionDraft = IOUUtils.shouldUseTransactionDraft(action); + const isTransactionDraft = shouldUseTransactionDraft(action); - IOU.setMoneyRequestCreated(transaction?.transactionID ?? '-1', newCreated, isTransactionDraft); + setMoneyRequestCreated(transactionID, newCreated, isTransactionDraft); if (isEditing) { - IOU.updateMoneyRequestDate(transaction?.transactionID ?? '-1', reportID, newCreated, policy, policyTags, policyCategories); + updateMoneyRequestDate(transactionID, reportID, newCreated, policy, policyTags, policyCategories); } navigateBack(); @@ -125,6 +113,7 @@ function IOURequestStepDate({ onSubmit={updateDate} submitButtonText={translate('common.save')} enabledWhenOffline + shouldHideFixErrorsAlert > ({ - splitDraftTransaction: { - key: ({route}) => { - const transactionID = route?.params.transactionID ?? -1; - return `${ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT}${transactionID}`; - }, - }, - reportActions: { - key: ({ - route: { - params: {action, iouType}, - }, - report, - }) => { - let reportID; - if (action === CONST.IOU.ACTION.EDIT) { - reportID = iouType === CONST.IOU.TYPE.SPLIT ? report?.reportID : report?.parentReportID; - } - return `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID ?? '-1'}`; - }, - canEvict: false, - }, - policy: { - key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report ? report.policyID : '-1'}`, - }, - policyCategories: { - key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${report ? report.policyID : '-1'}`, - }, - policyTags: { - key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_TAGS}${report ? report.policyID : '-1'}`, - }, - session: { - key: ONYXKEYS.SESSION, - }, -})(IOURequestStepDate); - // eslint-disable-next-line rulesdir/no-negated-variables -const IOURequestStepDateWithFullTransactionOrNotFound = withFullTransactionOrNotFound(IOURequestStepDateWithOnyx); +const IOURequestStepDateWithFullTransactionOrNotFound = withFullTransactionOrNotFound(IOURequestStepDate); // eslint-disable-next-line rulesdir/no-negated-variables const IOURequestStepDateWithWritableReportOrNotFound = withWritableReportOrNotFound(IOURequestStepDateWithFullTransactionOrNotFound); diff --git a/src/pages/iou/request/step/IOURequestStepDescription.tsx b/src/pages/iou/request/step/IOURequestStepDescription.tsx index 70bb913d0738..7e67307ae5aa 100644 --- a/src/pages/iou/request/step/IOURequestStepDescription.tsx +++ b/src/pages/iou/request/step/IOURequestStepDescription.tsx @@ -152,6 +152,7 @@ function IOURequestStepDescription({ validate={validate} submitButtonText={translate('common.save')} enabledWhenOffline + shouldHideFixErrorsAlert > ; - - userLocation: OnyxEntry; +type IOURequestStepWaypointProps = WithWritableReportOrNotFoundProps & { + transaction: OnyxEntry; }; -type IOURequestStepWaypointProps = IOURequestStepWaypointOnyxProps & - WithWritableReportOrNotFoundProps & { - transaction: OnyxEntry; - }; - function IOURequestStepWaypoint({ route: { params: {action, backTo, iouType, pageIndex, reportID, transactionID}, }, transaction, - recentWaypoints = [], - userLocation, }: IOURequestStepWaypointProps) { const styles = useThemeStyles(); const {windowWidth} = useWindowDimensions(); @@ -65,12 +54,31 @@ function IOURequestStepWaypoint({ const isFocused = navigation.isFocused(); const {translate} = useLocalize(); const {isOffline} = useNetwork(); + const [userLocation] = useOnyx(ONYXKEYS.USER_LOCATION); + const [recentWaypoints = []] = useOnyx(ONYXKEYS.NVP_RECENT_WAYPOINTS, { + // Only grab the most recent 20 waypoints because that's all that is shown in the UI. This also puts them into the format of data + // that the google autocomplete component expects for it's "predefined places" feature. + selector: (waypoints) => + (waypoints ? waypoints.slice(0, CONST.RECENT_WAYPOINTS_NUMBER as number) : []) + .filter((waypoint) => waypoint.keyForList?.includes(CONST.YOUR_LOCATION_TEXT) !== true) + .map((waypoint) => ({ + name: waypoint.name, + description: waypoint.address ?? '', + geometry: { + location: { + lat: waypoint.lat ?? 0, + lng: waypoint.lng ?? 0, + }, + }, + })), + }); const textInput = useRef(null); const parsedWaypointIndex = parseInt(pageIndex, 10); const allWaypoints = transaction?.comment?.waypoints ?? {}; const currentWaypoint = allWaypoints[`waypoint${pageIndex}`] ?? {}; const waypointCount = Object.keys(allWaypoints).length; const filledWaypointCount = Object.values(allWaypoints).filter((waypoint) => !isEmptyObject(waypoint)).length; + const shouldUseTransactionDraft = shouldUseTransactionDraftIOUUtils(action); const waypointDescriptionKey = useMemo(() => { switch (parsedWaypointIndex) { @@ -100,26 +108,26 @@ function IOURequestStepWaypoint({ const validate = (values: FormOnyxValues<'waypointForm'>): Partial> => { const errors = {}; const waypointValue = values[`waypoint${pageIndex}`] ?? ''; - if (isOffline && waypointValue !== '' && !ValidationUtils.isValidAddress(waypointValue)) { - ErrorUtils.addErrorMessage(errors, `waypoint${pageIndex}`, translate('bankAccount.error.address')); + if (isOffline && waypointValue !== '' && !isValidAddress(waypointValue)) { + addErrorMessage(errors, `waypoint${pageIndex}`, translate('bankAccount.error.address')); } // If the user is online, and they are trying to save a value without using the autocomplete, show an error message instructing them to use a selected address instead. // That enables us to save the address with coordinates when it is selected if (!isOffline && waypointValue !== '' && waypointAddress !== waypointValue) { - ErrorUtils.addErrorMessage(errors, `waypoint${pageIndex}`, translate('distance.error.selectSuggestedAddress')); + addErrorMessage(errors, `waypoint${pageIndex}`, translate('distance.error.selectSuggestedAddress')); } return errors; }; - const saveWaypoint = (waypoint: FormOnyxValues<'waypointForm'>) => Transaction.saveWaypoint(transactionID, pageIndex, waypoint, IOUUtils.shouldUseTransactionDraft(action)); + const saveWaypoint = (waypoint: FormOnyxValues<'waypointForm'>) => saveWaypointAction(transactionID, pageIndex, waypoint, shouldUseTransactionDraft); const submit = (values: FormOnyxValues<'waypointForm'>) => { const waypointValue = values[`waypoint${pageIndex}`] ?? ''; // Allows letting you set a waypoint to an empty value if (waypointValue === '') { - Transaction.removeWaypoint(transaction, pageIndex, IOUUtils.shouldUseTransactionDraft(action)); + removeWaypoint(transaction, pageIndex, shouldUseTransactionDraft); } // While the user is offline, the auto-complete address search will not work @@ -140,7 +148,7 @@ function IOURequestStepWaypoint({ }; const deleteStopAndHideModal = () => { - Transaction.removeWaypoint(transaction, pageIndex, IOUUtils.shouldUseTransactionDraft(action)); + removeWaypoint(transaction, pageIndex, shouldUseTransactionDraft); setRestoreFocusType(CONST.MODAL.RESTORE_FOCUS_TYPE.DELETE); setIsDeleteStopModalOpen(false); goBack(); @@ -155,7 +163,7 @@ function IOURequestStepWaypoint({ keyForList: `${values.name ?? 'waypoint'}_${Date.now()}`, }; - Transaction.saveWaypoint(transactionID, pageIndex, waypoint, IOUUtils.shouldUseTransactionDraft(action)); + saveWaypointAction(transactionID, pageIndex, waypoint, shouldUseTransactionDraft); goBack(); }; @@ -208,6 +216,7 @@ function IOURequestStepWaypoint({ shouldValidateOnChange={false} shouldValidateOnBlur={false} submitButtonText={translate('common.save')} + shouldHideFixErrorsAlert > ({ - userLocation: { - key: ONYXKEYS.USER_LOCATION, - }, - recentWaypoints: { - key: ONYXKEYS.NVP_RECENT_WAYPOINTS, - - // Only grab the most recent 20 waypoints because that's all that is shown in the UI. This also puts them into the format of data - // that the google autocomplete component expects for it's "predefined places" feature. - selector: (waypoints) => - (waypoints ? waypoints.slice(0, CONST.RECENT_WAYPOINTS_NUMBER as number) : []) - .filter((waypoint) => waypoint.keyForList?.includes(CONST.YOUR_LOCATION_TEXT) !== true) - .map((waypoint) => ({ - name: waypoint.name, - description: waypoint.address ?? '', - geometry: { - location: { - lat: waypoint.lat ?? 0, - lng: waypoint.lng ?? 0, - }, - }, - })), - }, - })(IOURequestStepWaypoint), - ), - true, -); +export default withWritableReportOrNotFound(withFullTransactionOrNotFound(IOURequestStepWaypoint), true); diff --git a/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx index 4e34fc47a6ed..4fdc94c49a0b 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx @@ -13,7 +13,7 @@ import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@navigation/Navigation'; -import * as ExitSurvey from '@userActions/ExitSurvey'; +import {saveExitReason} from '@userActions/ExitSurvey'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -68,12 +68,13 @@ function ExitSurveyReasonPage() { if (!reason) { return; } - ExitSurvey.saveExitReason(reason); + saveExitReason(reason); Navigation.navigate(ROUTES.SETTINGS_EXIT_SURVEY_RESPONSE.getRoute(reason, ROUTES.SETTINGS_EXIT_SURVEY_REASON.route)); }} submitButtonText={translate('common.next')} shouldValidateOnBlur shouldValidateOnChange + shouldHideFixErrorsAlert > {isOffline && } {!isOffline && ( diff --git a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx index ba451cf8ce55..4b26a5b2b898 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx @@ -111,6 +111,7 @@ function ExitSurveyResponsePage({route, navigation}: ExitSurveyResponsePageProps }} shouldValidateOnBlur shouldValidateOnChange + shouldHideFixErrorsAlert > {isOffline && } {!isOffline && ( diff --git a/src/pages/settings/Profile/Contacts/NewContactMethodPage.tsx b/src/pages/settings/Profile/Contacts/NewContactMethodPage.tsx index c49c286e9749..0f1ad2db5595 100644 --- a/src/pages/settings/Profile/Contacts/NewContactMethodPage.tsx +++ b/src/pages/settings/Profile/Contacts/NewContactMethodPage.tsx @@ -144,6 +144,7 @@ function NewContactMethodPage({route}: NewContactMethodPageProps) { onSubmit={handleValidateMagicCode} submitButtonText={translate('common.add')} style={[styles.flexGrow1, styles.mh5]} + shouldHideFixErrorsAlert > {translate('common.pleaseEnterEmailOrPhoneNumber')} diff --git a/src/pages/settings/Profile/CustomStatus/SetDatePage.tsx b/src/pages/settings/Profile/CustomStatus/SetDatePage.tsx index 51dddadbc8d1..7886e89e05a9 100644 --- a/src/pages/settings/Profile/CustomStatus/SetDatePage.tsx +++ b/src/pages/settings/Profile/CustomStatus/SetDatePage.tsx @@ -1,46 +1,40 @@ import React, {useCallback} from 'react'; -import {withOnyx} from 'react-native-onyx'; -import type {OnyxEntry} from 'react-native-onyx'; +import {useOnyx} from 'react-native-onyx'; import DatePicker from '@components/DatePicker'; import FormProvider from '@components/Form/FormProvider'; import InputWrapper from '@components/Form/InputWrapper'; import type {FormOnyxValues} from '@components/Form/types'; +import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as User from '@libs/actions/User'; import DateUtils from '@libs/DateUtils'; import Navigation from '@libs/Navigation/Navigation'; -import * as ValidationUtils from '@libs/ValidationUtils'; +import {getDatePassedError, getFieldRequiredErrors} from '@libs/ValidationUtils'; +import {updateDraftCustomStatus} from '@userActions/User'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import INPUT_IDS from '@src/types/form/SettingsStatusClearDateForm'; -import type * as OnyxTypes from '@src/types/onyx'; +import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; type DateTime = { dateTime: string; }; - -type SetDatePageOnyxProps = { - customStatus: OnyxEntry; -}; - -type SetDatePageProps = SetDatePageOnyxProps; - -function SetDatePage({customStatus}: SetDatePageProps) { +function SetDatePage() { const styles = useThemeStyles(); const {translate} = useLocalize(); + const [customStatus, customStatusMetadata] = useOnyx(ONYXKEYS.CUSTOM_STATUS_DRAFT); const customClearAfter = customStatus?.clearAfter ?? ''; const onSubmit = (value: DateTime) => { - User.updateDraftCustomStatus({clearAfter: DateUtils.combineDateAndTime(customClearAfter, value.dateTime)}); + updateDraftCustomStatus({clearAfter: DateUtils.combineDateAndTime(customClearAfter, value.dateTime)}); Navigation.goBack(ROUTES.SETTINGS_STATUS_CLEAR_AFTER); }; const validate = useCallback((values: FormOnyxValues) => { - const errors = ValidationUtils.getFieldRequiredErrors(values, [INPUT_IDS.DATE_TIME]); - const dateError = ValidationUtils.getDatePassedError(values.dateTime); + const errors = getFieldRequiredErrors(values, [INPUT_IDS.DATE_TIME]); + const dateError = getDatePassedError(values.dateTime); if (values.dateTime && dateError) { errors.dateTime = dateError; @@ -49,6 +43,10 @@ function SetDatePage({customStatus}: SetDatePageProps) { return errors; }, []); + if (isLoadingOnyxValue(customStatusMetadata)) { + return ; + } + return ( ({ - customStatus: { - key: ONYXKEYS.CUSTOM_STATUS_DRAFT, - }, -})(SetDatePage); +export default SetDatePage; diff --git a/src/pages/settings/Profile/CustomStatus/StatusClearAfterPage.tsx b/src/pages/settings/Profile/CustomStatus/StatusClearAfterPage.tsx index 55c0100a84db..412f52ed9656 100644 --- a/src/pages/settings/Profile/CustomStatus/StatusClearAfterPage.tsx +++ b/src/pages/settings/Profile/CustomStatus/StatusClearAfterPage.tsx @@ -11,10 +11,10 @@ import Text from '@components/Text'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as User from '@libs/actions/User'; import DateUtils from '@libs/DateUtils'; import Navigation from '@libs/Navigation/Navigation'; -import * as ValidationUtils from '@libs/ValidationUtils'; +import {validateDateTimeIsAtLeastOneMinuteInFuture} from '@libs/ValidationUtils'; +import {updateDraftCustomStatus} from '@userActions/User'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -47,7 +47,7 @@ const useValidateCustomDate = (data: string) => { const [customDateError, setCustomDateError] = useState(''); const [customTimeError, setCustomTimeError] = useState(''); const validate = () => { - const {dateValidationErrorKey, timeValidationErrorKey} = ValidationUtils.validateDateTimeIsAtLeastOneMinuteInFuture(data); + const {dateValidationErrorKey, timeValidationErrorKey} = validateDateTimeIsAtLeastOneMinuteInFuture(data); setCustomDateError(dateValidationErrorKey); setCustomTimeError(timeValidationErrorKey); @@ -113,7 +113,7 @@ function StatusClearAfterPage() { const selectedRange = statusType.find((item) => item.isSelected); calculatedDraftDate = DateUtils.getDateFromStatusType(selectedRange?.value ?? CONST.CUSTOM_STATUS_TYPES.NEVER); } - User.updateDraftCustomStatus({clearAfter: calculatedDraftDate}); + updateDraftCustomStatus({clearAfter: calculatedDraftDate}); Navigation.goBack(ROUTES.SETTINGS_STATUS); }; @@ -125,11 +125,11 @@ function StatusClearAfterPage() { setDraftPeriod(mode.value); if (mode.value === CONST.CUSTOM_STATUS_TYPES.CUSTOM) { - User.updateDraftCustomStatus({clearAfter: DateUtils.getOneHourFromNow()}); + updateDraftCustomStatus({clearAfter: DateUtils.getOneHourFromNow()}); } else { const selectedRange = statusType.find((item) => item.value === mode.value); const calculatedDraftDate = DateUtils.getDateFromStatusType(selectedRange?.value ?? CONST.CUSTOM_STATUS_TYPES.NEVER); - User.updateDraftCustomStatus({clearAfter: calculatedDraftDate}); + updateDraftCustomStatus({clearAfter: calculatedDraftDate}); Navigation.goBack(ROUTES.SETTINGS_STATUS); } }, @@ -137,7 +137,7 @@ function StatusClearAfterPage() { ); useEffect(() => { - User.updateDraftCustomStatus({ + updateDraftCustomStatus({ clearAfter: draftClearAfter || clearAfter, }); @@ -179,6 +179,7 @@ function StatusClearAfterPage() { scrollContextEnabled={false} isSubmitButtonVisible={false} enabledWhenOffline + shouldHideFixErrorsAlert > {timePeriodOptions()} diff --git a/src/pages/settings/Profile/PersonalDetails/DateOfBirthPage.tsx b/src/pages/settings/Profile/PersonalDetails/DateOfBirthPage.tsx index 0d400211cc4c..81197aabf401 100644 --- a/src/pages/settings/Profile/PersonalDetails/DateOfBirthPage.tsx +++ b/src/pages/settings/Profile/PersonalDetails/DateOfBirthPage.tsx @@ -12,8 +12,8 @@ import ScreenWrapper from '@components/ScreenWrapper'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@libs/Navigation/Navigation'; -import * as ValidationUtils from '@libs/ValidationUtils'; -import * as PersonalDetails from '@userActions/PersonalDetails'; +import {getAgeRequirementError, getFieldRequiredErrors} from '@libs/ValidationUtils'; +import {updateDateOfBirth} from '@userActions/PersonalDetails'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import INPUT_IDS from '@src/types/form/DateOfBirthForm'; @@ -28,11 +28,11 @@ function DateOfBirthPage() { */ const validate = useCallback((values: FormOnyxValues) => { const requiredFields = ['dob' as const]; - const errors = ValidationUtils.getFieldRequiredErrors(values, requiredFields); + const errors = getFieldRequiredErrors(values, requiredFields); const minimumAge = CONST.DATE_BIRTH.MIN_AGE; const maximumAge = CONST.DATE_BIRTH.MAX_AGE; - const dateError = ValidationUtils.getAgeRequirementError(values.dob ?? '', minimumAge, maximumAge); + const dateError = getAgeRequirementError(values.dob ?? '', minimumAge, maximumAge); if (values.dob && dateError) { errors.dob = dateError; @@ -58,9 +58,10 @@ function DateOfBirthPage() { style={[styles.flexGrow1, styles.ph5]} formID={ONYXKEYS.FORMS.DATE_OF_BIRTH_FORM} validate={validate} - onSubmit={PersonalDetails.updateDateOfBirth} + onSubmit={updateDateOfBirth} submitButtonText={translate('common.save')} enabledWhenOffline + shouldHideFixErrorsAlert > ): FormInputErrors => { - const errors = ValidationUtils.getFieldRequiredErrors(values, [INPUT_IDS.SUBSCRIPTION_SIZE]); - if (values[INPUT_IDS.SUBSCRIPTION_SIZE] && !ValidationUtils.isValidSubscriptionSize(values[INPUT_IDS.SUBSCRIPTION_SIZE])) { + const errors = getFieldRequiredErrors(values, [INPUT_IDS.SUBSCRIPTION_SIZE]); + if (values[INPUT_IDS.SUBSCRIPTION_SIZE] && !isValidSubscriptionSize(values[INPUT_IDS.SUBSCRIPTION_SIZE])) { errors.subscriptionSize = translate('subscription.subscriptionSize.error.size'); } @@ -59,6 +59,7 @@ function Size({onNext}: SizeProps) { validate={validate} style={[styles.mh5, styles.flexGrow1]} enabledWhenOffline + shouldHideFixErrorsAlert > {translate('subscription.subscriptionSize.yourSize')} diff --git a/src/pages/tasks/NewTaskDescriptionPage.tsx b/src/pages/tasks/NewTaskDescriptionPage.tsx index e70b6ea15d20..46df166b1eae 100644 --- a/src/pages/tasks/NewTaskDescriptionPage.tsx +++ b/src/pages/tasks/NewTaskDescriptionPage.tsx @@ -1,10 +1,10 @@ import React from 'react'; import {View} from 'react-native'; -import {withOnyx} from 'react-native-onyx'; -import type {OnyxEntry} from 'react-native-onyx'; +import {useOnyx} from 'react-native-onyx'; import FormProvider from '@components/Form/FormProvider'; import InputWrapperWithRef from '@components/Form/InputWrapper'; import type {FormInputErrors, FormOnyxValues} from '@components/Form/types'; +import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import TextInput from '@components/TextInput'; @@ -25,18 +25,14 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import INPUT_IDS from '@src/types/form/NewTaskForm'; -import type {Task} from '@src/types/onyx'; +import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; -type NewTaskDescriptionPageOnyxProps = { - /** Task Creation Data */ - task: OnyxEntry; -}; +type NewTaskDescriptionPageProps = PlatformStackScreenProps; -type NewTaskDescriptionPageProps = NewTaskDescriptionPageOnyxProps & PlatformStackScreenProps; - -function NewTaskDescriptionPage({task, route}: NewTaskDescriptionPageProps) { +function NewTaskDescriptionPage({route}: NewTaskDescriptionPageProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); + const [task, taskMetadata] = useOnyx(ONYXKEYS.TASK); const {inputCallbackRef, inputRef} = useAutoFocusInput(); const goBack = () => Navigation.goBack(ROUTES.NEW_TASK.getRoute(route.params?.backTo)); @@ -55,6 +51,10 @@ function NewTaskDescriptionPage({task, route}: NewTaskDescriptionPageProps) { return errors; }; + if (isLoadingOnyxValue(taskMetadata)) { + return ; + } + return ( ({ - task: { - key: ONYXKEYS.TASK, - }, -})(NewTaskDescriptionPage); +export default NewTaskDescriptionPage; diff --git a/src/pages/tasks/NewTaskTitlePage.tsx b/src/pages/tasks/NewTaskTitlePage.tsx index 88a44aca8501..cd29082aced3 100644 --- a/src/pages/tasks/NewTaskTitlePage.tsx +++ b/src/pages/tasks/NewTaskTitlePage.tsx @@ -1,38 +1,34 @@ import React from 'react'; import {View} from 'react-native'; -import {withOnyx} from 'react-native-onyx'; -import type {OnyxEntry} from 'react-native-onyx'; +import {useOnyx} from 'react-native-onyx'; import FormProvider from '@components/Form/FormProvider'; import InputWrapperWithRef from '@components/Form/InputWrapper'; import type {FormInputErrors, FormOnyxValues} from '@components/Form/types'; +import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import TextInput from '@components/TextInput'; import useAutoFocusInput from '@hooks/useAutoFocusInput'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as ErrorUtils from '@libs/ErrorUtils'; +import {addErrorMessage} from '@libs/ErrorUtils'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {NewTaskNavigatorParamList} from '@libs/Navigation/types'; -import * as TaskActions from '@userActions/Task'; +import {setTitleValue} from '@userActions/Task'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import INPUT_IDS from '@src/types/form/NewTaskForm'; -import type {Task} from '@src/types/onyx'; +import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; -type NewTaskTitlePageOnyxProps = { - /** Task Creation Data */ - task: OnyxEntry; -}; -type NewTaskTitlePageProps = NewTaskTitlePageOnyxProps & PlatformStackScreenProps; +type NewTaskTitlePageProps = PlatformStackScreenProps; -function NewTaskTitlePage({task, route}: NewTaskTitlePageProps) { +function NewTaskTitlePage({route}: NewTaskTitlePageProps) { const styles = useThemeStyles(); const {inputCallbackRef} = useAutoFocusInput(); - + const [task, taskMetadata] = useOnyx(ONYXKEYS.TASK); const {translate} = useLocalize(); const goBack = () => Navigation.goBack(ROUTES.NEW_TASK.getRoute(route.params?.backTo)); @@ -41,9 +37,9 @@ function NewTaskTitlePage({task, route}: NewTaskTitlePageProps) { if (!values.taskTitle) { // We error if the user doesn't enter a task name - ErrorUtils.addErrorMessage(errors, 'taskTitle', translate('newTaskPage.pleaseEnterTaskName')); + addErrorMessage(errors, 'taskTitle', translate('newTaskPage.pleaseEnterTaskName')); } else if (values.taskTitle.length > CONST.TITLE_CHARACTER_LIMIT) { - ErrorUtils.addErrorMessage(errors, 'taskTitle', translate('common.error.characterLimitExceedCounter', {length: values.taskTitle.length, limit: CONST.TITLE_CHARACTER_LIMIT})); + addErrorMessage(errors, 'taskTitle', translate('common.error.characterLimitExceedCounter', {length: values.taskTitle.length, limit: CONST.TITLE_CHARACTER_LIMIT})); } return errors; @@ -52,10 +48,14 @@ function NewTaskTitlePage({task, route}: NewTaskTitlePageProps) { // On submit, we want to call the assignTask function and wait to validate // the response const onSubmit = (values: FormOnyxValues) => { - TaskActions.setTitleValue(values.taskTitle); + setTitleValue(values.taskTitle); goBack(); }; + if (isLoadingOnyxValue(taskMetadata)) { + return ; + } + return ( ({ - task: { - key: ONYXKEYS.TASK, - }, -})(NewTaskTitlePage); +export default NewTaskTitlePage; diff --git a/src/pages/tasks/TaskDescriptionPage.tsx b/src/pages/tasks/TaskDescriptionPage.tsx index 9fdf1757ca96..bc4fad61aaaa 100644 --- a/src/pages/tasks/TaskDescriptionPage.tsx +++ b/src/pages/tasks/TaskDescriptionPage.tsx @@ -110,6 +110,7 @@ function TaskDescriptionPage({report, currentUserPersonalDetails}: TaskDescripti onSubmit={submit} submitButtonText={translate('common.save')} enabledWhenOffline + shouldHideFixErrorsAlert > = {}; if (!title) { - ErrorUtils.addErrorMessage(errors, INPUT_IDS.TITLE, translate('newTaskPage.pleaseEnterTaskName')); + addErrorMessage(errors, INPUT_IDS.TITLE, translate('newTaskPage.pleaseEnterTaskName')); } else if (title.length > CONST.TITLE_CHARACTER_LIMIT) { - ErrorUtils.addErrorMessage(errors, INPUT_IDS.TITLE, translate('common.error.characterLimitExceedCounter', {length: title.length, limit: CONST.TITLE_CHARACTER_LIMIT})); + addErrorMessage(errors, INPUT_IDS.TITLE, translate('common.error.characterLimitExceedCounter', {length: title.length, limit: CONST.TITLE_CHARACTER_LIMIT})); } return errors; @@ -54,7 +54,7 @@ function TaskTitlePage({report, currentUserPersonalDetails}: TaskTitlePageProps) if (values.title !== report?.reportName && !isEmptyObject(report)) { // Set the title of the report in the store and then call EditTask API // to update the title of the report on the server - Task.editTask(report, {title: values.title}); + editTask(report, {title: values.title}); } Navigation.dismissModal(report?.reportID); @@ -62,16 +62,16 @@ function TaskTitlePage({report, currentUserPersonalDetails}: TaskTitlePageProps) [report], ); - if (!ReportUtils.isTaskReport(report)) { + if (!isTaskReport(report)) { Navigation.isNavigationReady().then(() => { Navigation.dismissModal(report?.reportID); }); } const inputRef = useRef(null); - const isOpen = ReportUtils.isOpenTaskReport(report); - const canModifyTask = Task.canModifyTask(report, currentUserPersonalDetails.accountID); - const isTaskNonEditable = ReportUtils.isTaskReport(report) && (!canModifyTask || !isOpen); + const isOpen = isOpenTaskReport(report); + const canModifyTask = canModifyTaskAction(report, currentUserPersonalDetails.accountID); + const isTaskNonEditable = isTaskReport(report) && (!canModifyTask || !isOpen); return ( ): FormInputErrors => { const errors: FormInputErrors = {}; - if (values[params.expenseType] && !ValidationUtils.isNumeric(values[params.expenseType])) { - ErrorUtils.addErrorMessage(errors, params.expenseType, translate('workspace.netsuite.advancedConfig.error.customFormID')); + if (values[params.expenseType] && !isNumeric(values[params.expenseType])) { + addErrorMessage(errors, params.expenseType, translate('workspace.netsuite.advancedConfig.error.customFormID')); } return errors; @@ -55,7 +55,7 @@ function NetSuiteCustomFormIDPage({policy}: WithPolicyConnectionsProps) { const updateCustomFormID = useCallback( (formValues: FormOnyxValues) => { if (config?.customFormIDOptions?.[customFormIDKey]?.[CONST.NETSUITE_MAP_EXPORT_DESTINATION[exportDestination]] !== formValues[params.expenseType]) { - Connections.updateNetSuiteCustomFormIDOptions(policyID, formValues[params.expenseType], isReimbursable, exportDestination, config?.customFormIDOptions); + updateNetSuiteCustomFormIDOptions(policyID, formValues[params.expenseType], isReimbursable, exportDestination, config?.customFormIDOptions); } Navigation.goBack(ROUTES.POLICY_ACCOUNTING_NETSUITE_ADVANCED.getRoute(policyID)); }, @@ -86,12 +86,13 @@ function NetSuiteCustomFormIDPage({policy}: WithPolicyConnectionsProps) { submitButtonText={translate('common.confirm')} shouldValidateOnBlur shouldValidateOnChange + shouldHideFixErrorsAlert > Policy.clearNetSuiteErrorField(policyID, customFormIDKey)} + onClose={() => clearNetSuiteErrorField(policyID, customFormIDKey)} > customSegment?.[fieldName as keyof typeof customSegment]?.toLowerCase() === formValues[key].toLowerCase(), ) ) { - ErrorUtils.addErrorMessage(errors, fieldName, translate('workspace.netsuite.import.importCustomFields.customSegments.errors.uniqueFieldError', {fieldName: fieldLabel})); + addErrorMessage(errors, fieldName, translate('workspace.netsuite.import.importCustomFields.customSegments.errors.uniqueFieldError', {fieldName: fieldLabel})); } return errors; @@ -129,6 +129,7 @@ function NetSuiteImportCustomFieldEdit({ shouldValidateOnBlur shouldValidateOnChange isSubmitDisabled={!!settingsPendingAction([`${importCustomField}_${valueIndex}`], config?.pendingFields)} + shouldHideFixErrorsAlert > = { + const renderMap: Record = { mapping: renderSelection, }; @@ -174,7 +175,7 @@ function NetSuiteImportCustomFieldEdit({ contentContainerStyle={[styles.pb2, styles.flex1]} titleStyle={styles.ph5} connectionName={CONST.POLICY.CONNECTIONS.NAME.NETSUITE} - shouldBeBlocked={!customField || !PolicyUtils.isNetSuiteCustomFieldPropertyEditable(customField, fieldName)} + shouldBeBlocked={!customField || !isNetSuiteCustomFieldPropertyEditable(customField, fieldName)} shouldUseScrollView={false} > {renderMap[fieldName] || renderForm} diff --git a/src/pages/workspace/accounting/netsuite/import/NetSuiteImportCustomFieldNew/substeps/ChooseCustomListStep.tsx b/src/pages/workspace/accounting/netsuite/import/NetSuiteImportCustomFieldNew/substeps/ChooseCustomListStep.tsx index b51ad476a10b..fa674b52782c 100644 --- a/src/pages/workspace/accounting/netsuite/import/NetSuiteImportCustomFieldNew/substeps/ChooseCustomListStep.tsx +++ b/src/pages/workspace/accounting/netsuite/import/NetSuiteImportCustomFieldNew/substeps/ChooseCustomListStep.tsx @@ -6,7 +6,7 @@ import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useNetSuiteImportAddCustomListFormSubmit from '@hooks/useNetSuiteImportAddCustomListForm'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as ValidationUtils from '@libs/ValidationUtils'; +import {getFieldRequiredErrors} from '@libs/ValidationUtils'; import NetSuiteCustomListPicker from '@pages/workspace/accounting/netsuite/import/NetSuiteImportCustomFieldNew/NetSuiteCustomListPicker'; import type {CustomFieldSubStepWithPolicy} from '@pages/workspace/accounting/netsuite/types'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -19,7 +19,7 @@ function ChooseCustomListStep({policy, onNext, isEditing, netSuiteCustomFieldFor const {translate} = useLocalize(); const validate = useCallback((values: FormOnyxValues): FormInputErrors => { - return ValidationUtils.getFieldRequiredErrors(values, [INPUT_IDS.LIST_NAME]); + return getFieldRequiredErrors(values, [INPUT_IDS.LIST_NAME]); }, []); const handleSubmit = useNetSuiteImportAddCustomListFormSubmit({ @@ -39,6 +39,7 @@ function ChooseCustomListStep({policy, onNext, isEditing, netSuiteCustomFieldFor enabledWhenOffline submitFlexEnabled shouldUseScrollView + shouldHideFixErrorsAlert > {translate(`workspace.netsuite.import.importCustomFields.customLists.addForm.listNameTitle`)} ): FormInputErrors => { const errors: FormInputErrors = {}; - if (!ValidationUtils.isRequiredFulfilled(values[INPUT_IDS.INTERNAL_ID])) { + if (!isRequiredFulfilled(values[INPUT_IDS.INTERNAL_ID])) { errors[INPUT_IDS.INTERNAL_ID] = translate('workspace.netsuite.import.importCustomFields.requiredFieldError', {fieldName: fieldLabel}); } else if (customSegments?.find((customSegment) => customSegment.internalID.toLowerCase() === values[INPUT_IDS.INTERNAL_ID].toLowerCase())) { errors[INPUT_IDS.INTERNAL_ID] = translate('workspace.netsuite.import.importCustomFields.customSegments.errors.uniqueFieldError', {fieldName: fieldLabel}); @@ -59,6 +59,7 @@ function CustomSegmentInternalIdStep({customSegmentType, onNext, isEditing, netS enabledWhenOffline submitFlexEnabled shouldUseScrollView + shouldHideFixErrorsAlert > diff --git a/src/pages/workspace/accounting/netsuite/import/NetSuiteImportCustomFieldNew/substeps/CustomSegmentNameStep.tsx b/src/pages/workspace/accounting/netsuite/import/NetSuiteImportCustomFieldNew/substeps/CustomSegmentNameStep.tsx index ad3d4d8c39ca..b56c270f8eae 100644 --- a/src/pages/workspace/accounting/netsuite/import/NetSuiteImportCustomFieldNew/substeps/CustomSegmentNameStep.tsx +++ b/src/pages/workspace/accounting/netsuite/import/NetSuiteImportCustomFieldNew/substeps/CustomSegmentNameStep.tsx @@ -11,7 +11,7 @@ import useLocalize from '@hooks/useLocalize'; import useNetSuiteImportAddCustomSegmentFormSubmit from '@hooks/useNetSuiteImportAddCustomSegmentForm'; import useThemeStyles from '@hooks/useThemeStyles'; import Parser from '@libs/Parser'; -import * as ValidationUtils from '@libs/ValidationUtils'; +import {isRequiredFulfilled} from '@libs/ValidationUtils'; import type {CustomFieldSubStepWithPolicy} from '@pages/workspace/accounting/netsuite/types'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -37,7 +37,7 @@ function CustomSegmentNameStep({customSegmentType, onNext, isEditing, customSegm (values: FormOnyxValues): FormInputErrors => { const errors: FormInputErrors = {}; - if (!ValidationUtils.isRequiredFulfilled(values[INPUT_IDS.SEGMENT_NAME])) { + if (!isRequiredFulfilled(values[INPUT_IDS.SEGMENT_NAME])) { errors[INPUT_IDS.SEGMENT_NAME] = translate('workspace.netsuite.import.importCustomFields.requiredFieldError', { fieldName: translate(`workspace.netsuite.import.importCustomFields.customSegments.addForm.${customSegmentRecordType}Name`), }); @@ -60,6 +60,7 @@ function CustomSegmentNameStep({customSegmentType, onNext, isEditing, customSegm enabledWhenOffline submitFlexEnabled shouldUseScrollView + shouldHideFixErrorsAlert > diff --git a/src/pages/workspace/accounting/netsuite/import/NetSuiteImportCustomFieldNew/substeps/CustomSegmentScriptIdStep.tsx b/src/pages/workspace/accounting/netsuite/import/NetSuiteImportCustomFieldNew/substeps/CustomSegmentScriptIdStep.tsx index 529c720dfb9f..72dba52d8307 100644 --- a/src/pages/workspace/accounting/netsuite/import/NetSuiteImportCustomFieldNew/substeps/CustomSegmentScriptIdStep.tsx +++ b/src/pages/workspace/accounting/netsuite/import/NetSuiteImportCustomFieldNew/substeps/CustomSegmentScriptIdStep.tsx @@ -11,7 +11,7 @@ import useLocalize from '@hooks/useLocalize'; import useNetSuiteImportAddCustomSegmentFormSubmit from '@hooks/useNetSuiteImportAddCustomSegmentForm'; import useThemeStyles from '@hooks/useThemeStyles'; import Parser from '@libs/Parser'; -import * as ValidationUtils from '@libs/ValidationUtils'; +import {isRequiredFulfilled} from '@libs/ValidationUtils'; import type {CustomFieldSubStepWithPolicy} from '@pages/workspace/accounting/netsuite/types'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; @@ -36,7 +36,7 @@ function CustomSegmentScriptIdStep({customSegmentType, onNext, isEditing, custom (values: FormOnyxValues): FormInputErrors => { const errors: FormInputErrors = {}; - if (!ValidationUtils.isRequiredFulfilled(values[INPUT_IDS.SCRIPT_ID])) { + if (!isRequiredFulfilled(values[INPUT_IDS.SCRIPT_ID])) { errors[INPUT_IDS.SCRIPT_ID] = translate('workspace.netsuite.import.importCustomFields.requiredFieldError', {fieldName: fieldLabel}); } else if (customSegments?.find((customSegment) => customSegment.scriptID.toLowerCase() === values[INPUT_IDS.SCRIPT_ID].toLowerCase())) { errors[INPUT_IDS.SCRIPT_ID] = translate('workspace.netsuite.import.importCustomFields.customSegments.errors.uniqueFieldError', {fieldName: fieldLabel}); @@ -63,6 +63,7 @@ function CustomSegmentScriptIdStep({customSegmentType, onNext, isEditing, custom enabledWhenOffline submitFlexEnabled shouldUseScrollView + shouldHideFixErrorsAlert > diff --git a/src/pages/workspace/accounting/netsuite/import/NetSuiteImportCustomFieldNew/substeps/TransactionFieldIDStep.tsx b/src/pages/workspace/accounting/netsuite/import/NetSuiteImportCustomFieldNew/substeps/TransactionFieldIDStep.tsx index 1b787212f0ac..e7a6ea88ec98 100644 --- a/src/pages/workspace/accounting/netsuite/import/NetSuiteImportCustomFieldNew/substeps/TransactionFieldIDStep.tsx +++ b/src/pages/workspace/accounting/netsuite/import/NetSuiteImportCustomFieldNew/substeps/TransactionFieldIDStep.tsx @@ -11,7 +11,7 @@ import useLocalize from '@hooks/useLocalize'; import useNetSuiteImportAddCustomListFormSubmit from '@hooks/useNetSuiteImportAddCustomListForm'; import useThemeStyles from '@hooks/useThemeStyles'; import Parser from '@libs/Parser'; -import * as ValidationUtils from '@libs/ValidationUtils'; +import {isRequiredFulfilled} from '@libs/ValidationUtils'; import type {CustomFieldSubStepWithPolicy} from '@pages/workspace/accounting/netsuite/types'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -35,7 +35,7 @@ function TransactionFieldIDStep({onNext, isEditing, netSuiteCustomFieldFormValue const validate = useCallback( (values: FormOnyxValues): FormInputErrors => { const errors: FormInputErrors = {}; - if (!ValidationUtils.isRequiredFulfilled(values[INPUT_IDS.TRANSACTION_FIELD_ID])) { + if (!isRequiredFulfilled(values[INPUT_IDS.TRANSACTION_FIELD_ID])) { errors[INPUT_IDS.TRANSACTION_FIELD_ID] = translate('workspace.netsuite.import.importCustomFields.requiredFieldError', {fieldName: fieldLabel}); } else if (customLists?.find((customList) => customList.transactionFieldID.toLowerCase() === values[INPUT_IDS.TRANSACTION_FIELD_ID].toLowerCase())) { errors[INPUT_IDS.TRANSACTION_FIELD_ID] = translate('workspace.netsuite.import.importCustomFields.customLists.errors.uniqueTransactionFieldIDError'); @@ -56,6 +56,7 @@ function TransactionFieldIDStep({onNext, isEditing, netSuiteCustomFieldFormValue enabledWhenOffline submitFlexEnabled shouldUseScrollView + shouldHideFixErrorsAlert > {translate(`workspace.netsuite.import.importCustomFields.customLists.addForm.transactionFieldIDTitle`)} diff --git a/src/pages/workspace/accounting/netsuite/types.ts b/src/pages/workspace/accounting/netsuite/types.ts index 608dbadf316e..539518442865 100644 --- a/src/pages/workspace/accounting/netsuite/types.ts +++ b/src/pages/workspace/accounting/netsuite/types.ts @@ -74,6 +74,7 @@ type AccordionItem = { type ExpenseRouteParams = { expenseType: ValueOf; + policyID: string; }; type CustomFieldSubStepWithPolicy = SubStepProps & { diff --git a/src/pages/workspace/accounting/nsqs/NSQSSetupPage.tsx b/src/pages/workspace/accounting/nsqs/NSQSSetupPage.tsx index f3b578b6e064..cba771990413 100644 --- a/src/pages/workspace/accounting/nsqs/NSQSSetupPage.tsx +++ b/src/pages/workspace/accounting/nsqs/NSQSSetupPage.tsx @@ -88,6 +88,7 @@ function NSQSSetupPage({policy}: WithPolicyConnectionsProps) { enabledWhenOffline shouldValidateOnBlur shouldValidateOnChange + shouldHideFixErrorsAlert > {translate('workspace.nsqs.setup.description')} { - Category.setWorkspaceCategoryDescriptionHint(policyID, categoryName, commentHint); + setWorkspaceCategoryDescriptionHint(policyID, categoryName, commentHint); Navigation.setNavigationActionToMicrotaskQueue(() => Navigation.goBack(ROUTES.WORKSPACE_CATEGORY_SETTINGS.getRoute(policyID, categoryName))); }} submitButtonText={translate('common.save')} enabledWhenOffline + shouldHideFixErrorsAlert > {translate('workspace.rules.categoryRules.descriptionHintDescription', {categoryName})} diff --git a/src/pages/workspace/categories/CategoryFlagAmountsOverPage.tsx b/src/pages/workspace/categories/CategoryFlagAmountsOverPage.tsx index dd80757749dd..25508d3a3429 100644 --- a/src/pages/workspace/categories/CategoryFlagAmountsOverPage.tsx +++ b/src/pages/workspace/categories/CategoryFlagAmountsOverPage.tsx @@ -11,12 +11,12 @@ import useAutoFocusInput from '@hooks/useAutoFocusInput'; import useLocalize from '@hooks/useLocalize'; import usePolicy from '@hooks/usePolicy'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as CurrencyUtils from '@libs/CurrencyUtils'; +import {convertToFrontendAmountAsString, getCurrencySymbol} from '@libs/CurrencyUtils'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {SettingsNavigatorParamList} from '@navigation/types'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; -import * as Category from '@userActions/Policy/Category'; +import {setPolicyCategoryMaxAmount} from '@userActions/Policy/Category'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -45,7 +45,7 @@ function CategoryFlagAmountsOverPage({ const defaultValue = policyCategoryMaxExpenseAmount === CONST.DISABLED_MAX_EXPENSE_VALUE || !policyCategoryMaxExpenseAmount ? '' - : CurrencyUtils.convertToFrontendAmountAsString(policyCategoryMaxExpenseAmount, policy?.outputCurrency); + : convertToFrontendAmountAsString(policyCategoryMaxExpenseAmount, policy?.outputCurrency); return ( { - Category.setPolicyCategoryMaxAmount(policyID, categoryName, maxExpenseAmount, expenseLimitType); + setPolicyCategoryMaxAmount(policyID, categoryName, maxExpenseAmount, expenseLimitType); Navigation.setNavigationActionToMicrotaskQueue(() => Navigation.goBack(ROUTES.WORKSPACE_CATEGORY_SETTINGS.getRoute(policyID, categoryName))); }} submitButtonText={translate('workspace.editor.save')} enabledWhenOffline submitButtonStyles={styles.ph5} + shouldHideFixErrorsAlert > {translate('workspace.rules.categoryRules.flagAmountsOverDescription', {categoryName})} @@ -80,7 +81,7 @@ function CategoryFlagAmountsOverPage({ label={translate('iou.amount')} InputComponent={AmountForm} inputID={INPUT_IDS.MAX_EXPENSE_AMOUNT} - currency={CurrencyUtils.getCurrencySymbol(policy?.outputCurrency ?? CONST.CURRENCY.USD)} + currency={getCurrencySymbol(policy?.outputCurrency ?? CONST.CURRENCY.USD)} defaultValue={defaultValue} isCurrencyPressable={false} ref={inputCallbackRef} diff --git a/src/pages/workspace/categories/CategoryForm.tsx b/src/pages/workspace/categories/CategoryForm.tsx index f83c1f0594ad..a5b006cc3844 100644 --- a/src/pages/workspace/categories/CategoryForm.tsx +++ b/src/pages/workspace/categories/CategoryForm.tsx @@ -72,6 +72,7 @@ function CategoryForm({onSubmit, policyCategories, categoryName, validateEdit}: validate={validateEdit || validate} style={[styles.mh5, styles.flex1]} enabledWhenOffline + shouldHideFixErrorsAlert > ) => { - CompanyCards.updateCompanyCardName(workspaceAccountID, cardID, values[INPUT_IDS.NAME], bank, defaultValue); + updateCompanyCardName(workspaceAccountID, cardID, values[INPUT_IDS.NAME], bank, defaultValue); Navigation.goBack(); }; const validate = (values: FormOnyxValues): FormInputErrors => { - const errors = ValidationUtils.getFieldRequiredErrors(values, [INPUT_IDS.NAME]); + const errors = getFieldRequiredErrors(values, [INPUT_IDS.NAME]); const length = values.name.length; if (length > CONST.STANDARD_LENGTH_LIMIT) { - ErrorUtils.addErrorMessage(errors, INPUT_IDS.NAME, translate('common.error.characterLimitExceedCounter', {length, limit: CONST.STANDARD_LENGTH_LIMIT})); + addErrorMessage(errors, INPUT_IDS.NAME, translate('common.error.characterLimitExceedCounter', {length, limit: CONST.STANDARD_LENGTH_LIMIT})); } return errors; }; @@ -72,6 +72,7 @@ function WorkspaceCompanyCardEditCardNamePage({route}: WorkspaceCompanyCardEditC style={[styles.flex1, styles.mh5]} enabledWhenOffline validate={validate} + shouldHideFixErrorsAlert > ): FormInputErrors => { - const errors = ValidationUtils.getFieldRequiredErrors(values, [INPUT_IDS.CARD_TITLE]); + const errors = getFieldRequiredErrors(values, [INPUT_IDS.CARD_TITLE]); const length = values.cardTitle.length; if (length > CONST.STANDARD_LENGTH_LIMIT) { - ErrorUtils.addErrorMessage(errors, INPUT_IDS.CARD_TITLE, translate('common.error.characterLimitExceedCounter', {length, limit: CONST.STANDARD_LENGTH_LIMIT})); + addErrorMessage(errors, INPUT_IDS.CARD_TITLE, translate('common.error.characterLimitExceedCounter', {length, limit: CONST.STANDARD_LENGTH_LIMIT})); } return errors; }; const submit = (values: FormOnyxValues) => { - CompanyCards.setAddNewCompanyCardStepAndData({ + setAddNewCompanyCardStepAndData({ step: CONST.COMPANY_CARDS.STEP.CARD_DETAILS, data: { bankName: values.cardTitle, @@ -43,7 +43,7 @@ function CardNameStep() { }; const handleBackButtonPress = () => { - CompanyCards.setAddNewCompanyCardStepAndData({step: CONST.COMPANY_CARDS.STEP.CARD_INSTRUCTIONS}); + setAddNewCompanyCardStepAndData({step: CONST.COMPANY_CARDS.STEP.CARD_INSTRUCTIONS}); }; return ( @@ -65,6 +65,7 @@ function CardNameStep() { validate={validate} style={[styles.mh5, styles.flexGrow1]} enabledWhenOffline + shouldHideFixErrorsAlert > {!!feedProvider && !isStripeFeedProvider ? translate(`workspace.companyCards.addNewCard.feedDetails.${feedProvider}.title`) : ''} diff --git a/src/pages/workspace/companyCards/assignCard/CardNameStep.tsx b/src/pages/workspace/companyCards/assignCard/CardNameStep.tsx index 8bc88744e5b8..306df3a48229 100644 --- a/src/pages/workspace/companyCards/assignCard/CardNameStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/CardNameStep.tsx @@ -10,10 +10,10 @@ import TextInput from '@components/TextInput'; import useAutoFocusInput from '@hooks/useAutoFocusInput'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as ErrorUtils from '@libs/ErrorUtils'; -import * as ValidationUtils from '@libs/ValidationUtils'; +import {addErrorMessage} from '@libs/ErrorUtils'; +import {getFieldRequiredErrors} from '@libs/ValidationUtils'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; -import * as CompanyCards from '@userActions/CompanyCards'; +import {setAssignCardStepAndData} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import INPUT_IDS from '@src/types/form/EditExpensifyCardNameForm'; @@ -32,7 +32,7 @@ function CardNameStep({policyID}: CardNameStepProps) { const data = assignCard?.data; const submit = (values: FormOnyxValues) => { - CompanyCards.setAssignCardStepAndData({ + setAssignCardStepAndData({ currentStep: CONST.COMPANY_CARD.STEP.CONFIRMATION, data: { cardName: values.name, @@ -42,11 +42,11 @@ function CardNameStep({policyID}: CardNameStepProps) { }; const validate = (values: FormOnyxValues): FormInputErrors => { - const errors = ValidationUtils.getFieldRequiredErrors(values, [INPUT_IDS.NAME]); + const errors = getFieldRequiredErrors(values, [INPUT_IDS.NAME]); const length = values.name.length; if (length > CONST.STANDARD_LENGTH_LIMIT) { - ErrorUtils.addErrorMessage(errors, INPUT_IDS.NAME, translate('common.error.characterLimitExceedCounter', {length, limit: CONST.STANDARD_LENGTH_LIMIT})); + addErrorMessage(errors, INPUT_IDS.NAME, translate('common.error.characterLimitExceedCounter', {length, limit: CONST.STANDARD_LENGTH_LIMIT})); } return errors; @@ -64,7 +64,7 @@ function CardNameStep({policyID}: CardNameStepProps) { > CompanyCards.setAssignCardStepAndData({currentStep: CONST.COMPANY_CARD.STEP.CONFIRMATION, isEditing: false})} + onBackButtonPress={() => setAssignCardStepAndData({currentStep: CONST.COMPANY_CARD.STEP.CONFIRMATION, isEditing: false})} /> {translate('workspace.moreFeatures.companyCards.giveItNameInstruction')} ) => { - Policy.updateInvoiceCompanyName(policyID, values[INPUT_IDS.COMPANY_NAME]); + updateInvoiceCompanyName(policyID, values[INPUT_IDS.COMPANY_NAME]); Navigation.goBack(); }; const validate = (values: FormOnyxValues): FormInputErrors => - ValidationUtils.getFieldRequiredErrors(values, [INPUT_IDS.COMPANY_NAME]); + getFieldRequiredErrors(values, [INPUT_IDS.COMPANY_NAME]); return ( ) => { const companyWebsite = Str.sanitizeURL(values[INPUT_IDS.COMPANY_WEBSITE], CONST.COMPANY_WEBSITE_DEFAULT_SCHEME); - Policy.updateInvoiceCompanyWebsite(policyID, companyWebsite); + updateInvoiceCompanyWebsite(policyID, companyWebsite); Navigation.goBack(); }; const validate = ( values: FormOnyxValues, ): FormInputErrors => { - const errors = ValidationUtils.getFieldRequiredErrors(values, [INPUT_IDS.COMPANY_WEBSITE]); + const errors = getFieldRequiredErrors(values, [INPUT_IDS.COMPANY_WEBSITE]); if (values.companyWebsite) { const companyWebsite = Str.sanitizeURL(values.companyWebsite, CONST.COMPANY_WEBSITE_DEFAULT_SCHEME); - if (!ValidationUtils.isValidWebsite(companyWebsite)) { + if (!isValidWebsite(companyWebsite)) { errors.companyWebsite = translate('bankAccount.error.website'); } else { - const domain = Url.extractUrlDomain(companyWebsite); + const domain = extractUrlDomain(companyWebsite); if (!domain || !Str.isValidDomainName(domain)) { errors.companyWebsite = translate('iou.invalidDomainError'); - } else if (ValidationUtils.isPublicDomain(domain)) { + } else if (isPublicDomain(domain)) { errors.companyWebsite = translate('iou.publicDomainError'); } } @@ -81,6 +80,7 @@ function WorkspaceInvoicingDetailsWebsite({route}: WorkspaceInvoicingDetailsWebs style={[styles.flex1, styles.mh5]} enabledWhenOffline validate={validate} + shouldHideFixErrorsAlert > diff --git a/src/pages/workspace/perDiem/EditPerDiemSubratePage.tsx b/src/pages/workspace/perDiem/EditPerDiemSubratePage.tsx index b98adbb423d7..c3fb6d7f677e 100644 --- a/src/pages/workspace/perDiem/EditPerDiemSubratePage.tsx +++ b/src/pages/workspace/perDiem/EditPerDiemSubratePage.tsx @@ -91,6 +91,7 @@ function EditPerDiemSubratePage({route}: EditPerDiemSubratePageProps) { submitButtonText={translate('common.save')} style={[styles.mh5, styles.flex1]} enabledWhenOffline + shouldHideFixErrorsAlert > { - PolicyActions.setPolicyAutomaticApprovalLimit(policyID, maxExpenseAutoApprovalAmount); + setPolicyAutomaticApprovalLimit(policyID, maxExpenseAutoApprovalAmount); Navigation.setNavigationActionToMicrotaskQueue(Navigation.goBack); }} submitButtonText={translate('common.save')} enabledWhenOffline + shouldHideFixErrorsAlert > ; function RulesAutoPayReportsUnderPage({route}: RulesAutoPayReportsUnderPageProps) { - const policyID = route?.params?.policyID ?? '-1'; + const policyID = route.params.policyID; const policy = usePolicy(policyID); const {inputCallbackRef} = useAutoFocusInput(); const {translate} = useLocalize(); const styles = useThemeStyles(); - const currencySymbol = CurrencyUtils.getCurrencySymbol(policy?.outputCurrency ?? CONST.CURRENCY.USD); + const currencySymbol = getCurrencySymbol(policy?.outputCurrency ?? CONST.CURRENCY.USD); const autoPayApprovedReportsUnavailable = policy?.reimbursementChoice === CONST.POLICY.REIMBURSEMENT_CHOICES.REIMBURSEMENT_NO; - const defaultValue = CurrencyUtils.convertToFrontendAmountAsString(policy?.autoReimbursement?.limit ?? CONST.POLICY.AUTO_REIMBURSEMENT_DEFAULT_LIMIT_CENTS, policy?.outputCurrency); + const defaultValue = convertToFrontendAmountAsString(policy?.autoReimbursement?.limit ?? CONST.POLICY.AUTO_REIMBURSEMENT_DEFAULT_LIMIT_CENTS, policy?.outputCurrency); const validateLimit = ({maxExpenseAutoPayAmount}: FormOnyxValues) => { const errors: FormInputErrors = {}; - if (CurrencyUtils.convertToBackendAmount(parseFloat(maxExpenseAutoPayAmount)) > CONST.POLICY.AUTO_REIMBURSEMENT_MAX_LIMIT_CENTS) { + if (convertToBackendAmount(parseFloat(maxExpenseAutoPayAmount)) > CONST.POLICY.AUTO_REIMBURSEMENT_MAX_LIMIT_CENTS) { errors[INPUT_IDS.MAX_EXPENSE_AUTO_PAY_AMOUNT] = translate('workspace.rules.expenseReportRules.autoPayApprovedReportsLimitError', {currency: currencySymbol}); } return errors; @@ -65,11 +65,12 @@ function RulesAutoPayReportsUnderPage({route}: RulesAutoPayReportsUnderPageProps formID={ONYXKEYS.FORMS.RULES_AUTO_PAY_REPORTS_UNDER_MODAL_FORM} validate={validateLimit} onSubmit={({maxExpenseAutoPayAmount}) => { - PolicyActions.setPolicyAutoReimbursementLimit(policyID, maxExpenseAutoPayAmount); + setPolicyAutoReimbursementLimit(policyID, maxExpenseAutoPayAmount); Navigation.setNavigationActionToMicrotaskQueue(Navigation.goBack); }} submitButtonText={translate('common.save')} enabledWhenOffline + shouldHideFixErrorsAlert > { - PolicyActions.setPolicyMaxExpenseAge(policyID, maxExpenseAge); + setPolicyMaxExpenseAge(policyID, maxExpenseAge); Navigation.setNavigationActionToMicrotaskQueue(Navigation.goBack); }} submitButtonText={translate('workspace.editor.save')} enabledWhenOffline + shouldHideFixErrorsAlert > { - PolicyActions.setPolicyMaxExpenseAmount(policyID, maxExpenseAmount); + setPolicyMaxExpenseAmount(policyID, maxExpenseAmount); Navigation.setNavigationActionToMicrotaskQueue(Navigation.goBack); }} submitButtonText={translate('workspace.editor.save')} enabledWhenOffline + shouldHideFixErrorsAlert > ; function RulesRandomReportAuditPage({route}: RulesRandomReportAuditPageProps) { - const policyID = route?.params?.policyID ?? '-1'; + const policyID = route.params.policyID; const policy = usePolicy(policyID); const {inputCallbackRef} = useAutoFocusInput(); const {translate} = useLocalize(); const styles = useThemeStyles(); - const workflowApprovalsUnavailable = PolicyUtils.getWorkflowApprovalsUnavailable(policy); + const workflowApprovalsUnavailable = getWorkflowApprovalsUnavailable(policy); const defaultValue = Math.round((policy?.autoApproval?.auditRate ?? CONST.POLICY.RANDOM_AUDIT_DEFAULT_PERCENTAGE) * 100); return ( { - PolicyActions.setPolicyAutomaticApprovalRate(policyID, auditRatePercentage); + setPolicyAutomaticApprovalRate(policyID, auditRatePercentage); Navigation.setNavigationActionToMicrotaskQueue(Navigation.goBack); }} submitButtonText={translate('common.save')} enabledWhenOffline + shouldHideFixErrorsAlert > { - PolicyActions.setPolicyMaxExpenseAmountNoReceipt(policyID, maxExpenseAmountNoReceipt); + setPolicyMaxExpenseAmountNoReceipt(policyID, maxExpenseAmountNoReceipt); Navigation.setNavigationActionToMicrotaskQueue(Navigation.goBack); }} submitButtonText={translate('workspace.editor.save')} enabledWhenOffline + shouldHideFixErrorsAlert > PolicyUtils.getTagListName(policyTags, route.params.orderWeight), [policyTags, route.params.orderWeight]); + const taglistName = useMemo(() => getTagListName(policyTags, route.params.orderWeight), [policyTags, route.params.orderWeight]); const {inputCallbackRef} = useAutoFocusInput(); const backTo = route.params.backTo; const isQuickSettingsFlow = !!backTo; @@ -58,7 +58,7 @@ function WorkspaceEditTagsPage({route}: WorkspaceEditTagsPageProps) { const updateTaglistName = useCallback( (values: FormOnyxValues) => { if (values[INPUT_IDS.POLICY_TAGS_NAME] !== taglistName) { - Tag.renamePolicyTaglist(route.params.policyID, {oldName: taglistName, newName: values[INPUT_IDS.POLICY_TAGS_NAME]}, policyTags, route.params.orderWeight); + renamePolicyTaglist(route.params.policyID, {oldName: taglistName, newName: values[INPUT_IDS.POLICY_TAGS_NAME]}, policyTags, route.params.orderWeight); } goBackToTagsSettings(); }, @@ -87,6 +87,7 @@ function WorkspaceEditTagsPage({route}: WorkspaceEditTagsPageProps) { validate={validateTagName} submitButtonText={translate('common.save')} enabledWhenOffline + shouldHideFixErrorsAlert > diff --git a/src/pages/workspace/taxes/NamePage.tsx b/src/pages/workspace/taxes/NamePage.tsx index 963e47ce8566..dd6475812925 100644 --- a/src/pages/workspace/taxes/NamePage.tsx +++ b/src/pages/workspace/taxes/NamePage.tsx @@ -90,6 +90,7 @@ function NamePage({ onSubmit={submit} enabledWhenOffline validate={validate} + shouldHideFixErrorsAlert >