diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 0cc7934ad007..b4282cd8b842 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -373,7 +373,7 @@ type OnyxValues = { [ONYXKEYS.NETWORK]: OnyxTypes.Network; [ONYXKEYS.CUSTOM_STATUS_DRAFT]: OnyxTypes.CustomStatusDraft; [ONYXKEYS.INPUT_FOCUSED]: boolean; - [ONYXKEYS.PERSONAL_DETAILS_LIST]: Record; + [ONYXKEYS.PERSONAL_DETAILS_LIST]: OnyxTypes.PersonalDetailsList; [ONYXKEYS.PRIVATE_PERSONAL_DETAILS]: OnyxTypes.PrivatePersonalDetails; [ONYXKEYS.TASK]: OnyxTypes.Task; [ONYXKEYS.CURRENCY_LIST]: Record; diff --git a/src/components/ArchivedReportFooter.tsx b/src/components/ArchivedReportFooter.tsx index 7dadd86debfe..8604d20130c7 100644 --- a/src/components/ArchivedReportFooter.tsx +++ b/src/components/ArchivedReportFooter.tsx @@ -30,14 +30,14 @@ function ArchivedReportFooter({report, reportClosedAction, personalDetails = {}} const originalMessage = reportClosedAction?.actionName === CONST.REPORT.ACTIONS.TYPE.CLOSED ? reportClosedAction.originalMessage : null; const archiveReason = originalMessage?.reason ?? CONST.REPORT.ARCHIVE_REASON.DEFAULT; - let displayName = PersonalDetailsUtils.getDisplayNameOrDefault(personalDetails, [report.ownerAccountID, 'displayName']); + let displayName = PersonalDetailsUtils.getDisplayNameOrDefault(personalDetails?.[report?.ownerAccountID ?? 0]?.displayName); let oldDisplayName: string | undefined; if (archiveReason === CONST.REPORT.ARCHIVE_REASON.ACCOUNT_MERGED) { const newAccountID = originalMessage?.newAccountID; const oldAccountID = originalMessage?.oldAccountID; - displayName = PersonalDetailsUtils.getDisplayNameOrDefault(personalDetails, [newAccountID, 'displayName']); - oldDisplayName = PersonalDetailsUtils.getDisplayNameOrDefault(personalDetails, [oldAccountID, 'displayName']); + displayName = PersonalDetailsUtils.getDisplayNameOrDefault(personalDetails?.[newAccountID ?? 0]?.displayName); + oldDisplayName = PersonalDetailsUtils.getDisplayNameOrDefault(personalDetails?.[oldAccountID ?? 0]?.displayName); } const shouldRenderHTML = archiveReason !== CONST.REPORT.ARCHIVE_REASON.DEFAULT; diff --git a/src/components/withCurrentUserPersonalDetails.tsx b/src/components/withCurrentUserPersonalDetails.tsx index a97067c32c72..289e2254952f 100644 --- a/src/components/withCurrentUserPersonalDetails.tsx +++ b/src/components/withCurrentUserPersonalDetails.tsx @@ -37,7 +37,7 @@ export default function (accountPersonalDetails ? {...accountPersonalDetails, accountID} : {}), + () => (accountPersonalDetails ? {...accountPersonalDetails, accountID} : {}) as CurrentUserPersonalDetails, [accountPersonalDetails, accountID], ); return ( diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index cc7ef66f7a43..502775517fc1 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -518,7 +518,7 @@ function createOption(accountIDs, personalDetails, report, reportActions = {}, { (lastReportActions[report.reportID] && lastReportActions[report.reportID].originalMessage && lastReportActions[report.reportID].originalMessage.reason) || CONST.REPORT.ARCHIVE_REASON.DEFAULT; lastMessageText = Localize.translate(preferredLocale, `reportArchiveReasons.${archiveReason}`, { - displayName: archiveReason.displayName || PersonalDetailsUtils.getDisplayNameOrDefault(lastActorDetails, 'displayName'), + displayName: archiveReason.displayName || PersonalDetailsUtils.getDisplayNameOrDefault(lodashGet(lastActorDetails, 'displayName')), policyName: ReportUtils.getPolicyName(report), }); } diff --git a/src/libs/PersonalDetailsUtils.js b/src/libs/PersonalDetailsUtils.js deleted file mode 100644 index b5335eab0762..000000000000 --- a/src/libs/PersonalDetailsUtils.js +++ /dev/null @@ -1,222 +0,0 @@ -import lodashGet from 'lodash/get'; -import Onyx from 'react-native-onyx'; -import _ from 'underscore'; -import ONYXKEYS from '@src/ONYXKEYS'; -import * as LocalePhoneNumber from './LocalePhoneNumber'; -import * as Localize from './Localize'; -import * as UserUtils from './UserUtils'; - -let personalDetails = []; -let allPersonalDetails = {}; -Onyx.connect({ - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - callback: (val) => { - personalDetails = _.values(val); - allPersonalDetails = val; - }, -}); - -/** - * @param {Object | Null} passedPersonalDetails - * @param {Array | String} pathToDisplayName - * @param {String} [defaultValue] optional default display name value - * @returns {String} - */ -function getDisplayNameOrDefault(passedPersonalDetails, pathToDisplayName, defaultValue = '') { - const displayName = lodashGet(passedPersonalDetails, pathToDisplayName); - - return displayName || defaultValue || Localize.translateLocal('common.hidden'); -} - -/** - * Given a list of account IDs (as number) it will return an array of personal details objects. - * @param {Array} accountIDs - Array of accountIDs - * @param {Number} currentUserAccountID - * @param {Boolean} shouldChangeUserDisplayName - It will replace the current user's personal detail object's displayName with 'You'. - * @returns {Array} - Array of personal detail objects - */ -function getPersonalDetailsByIDs(accountIDs, currentUserAccountID, shouldChangeUserDisplayName = false) { - return _.chain(accountIDs) - .filter((accountID) => !!allPersonalDetails[accountID]) - .map((accountID) => { - const detail = allPersonalDetails[accountID]; - - if (shouldChangeUserDisplayName && currentUserAccountID === detail.accountID) { - return { - ...detail, - displayName: Localize.translateLocal('common.you'), - }; - } - - return detail; - }) - .value(); -} - -/** - * Given a list of logins, find the associated personal detail and return related accountIDs. - * - * @param {Array} logins Array of user logins - * @returns {Array} - Array of accountIDs according to passed logins - */ -function getAccountIDsByLogins(logins) { - return _.reduce( - logins, - (foundAccountIDs, login) => { - const currentDetail = _.find(personalDetails, (detail) => detail.login === login); - if (!currentDetail) { - // generate an account ID because in this case the detail is probably new, so we don't have a real accountID yet - foundAccountIDs.push(UserUtils.generateAccountID(login)); - } else { - foundAccountIDs.push(Number(currentDetail.accountID)); - } - return foundAccountIDs; - }, - [], - ); -} - -/** - * Given a list of accountIDs, find the associated personal detail and return related logins. - * - * @param {Array} accountIDs Array of user accountIDs - * @returns {Array} - Array of logins according to passed accountIDs - */ -function getLoginsByAccountIDs(accountIDs) { - return _.reduce( - accountIDs, - (foundLogins, accountID) => { - const currentDetail = _.find(personalDetails, (detail) => Number(detail.accountID) === Number(accountID)) || {}; - if (currentDetail.login) { - foundLogins.push(currentDetail.login); - } - return foundLogins; - }, - [], - ); -} - -/** - * Given a list of logins and accountIDs, return Onyx data for users with no existing personal details stored - * - * @param {Array} logins Array of user logins - * @param {Array} accountIDs Array of user accountIDs - * @returns {Object} - Object with optimisticData, successData and failureData (object of personal details objects) - */ -function getNewPersonalDetailsOnyxData(logins, accountIDs) { - const optimisticData = {}; - const successData = {}; - const failureData = {}; - - _.each(logins, (login, index) => { - const accountID = accountIDs[index]; - - if (_.isEmpty(allPersonalDetails[accountID])) { - optimisticData[accountID] = { - login, - accountID, - avatar: UserUtils.getDefaultAvatarURL(accountID), - displayName: LocalePhoneNumber.formatPhoneNumber(login), - }; - - /** - * Cleanup the optimistic user to ensure it does not permanently persist. - * This is done to prevent duplicate entries (upon success) since the BE will return other personal details with the correct account IDs. - */ - successData[accountID] = null; - } - }); - - return { - optimisticData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - value: optimisticData, - }, - ], - successData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - value: successData, - }, - ], - failureData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - value: failureData, - }, - ], - }; -} - -/** - * Applies common formatting to each piece of an address - * - * @param {String} piece - address piece to format - * @returns {String} - formatted piece - */ -function formatPiece(piece) { - return piece ? `${piece}, ` : ''; -} - -/** - * - * @param {String} street1 - street line 1 - * @param {String} street2 - street line 2 - * @returns {String} formatted street - */ -function getFormattedStreet(street1 = '', street2 = '') { - return `${street1}\n${street2}`; -} - -/** - * - * @param {*} street - formatted address - * @returns {[string, string]} [street1, street2] - */ -function getStreetLines(street = '') { - const streets = street.split('\n'); - return [streets[0], streets[1]]; -} - -/** - * Formats an address object into an easily readable string - * - * @param {OnyxTypes.PrivatePersonalDetails} privatePersonalDetails - details object - * @returns {String} - formatted address - */ -function getFormattedAddress(privatePersonalDetails) { - const {address} = privatePersonalDetails; - const [street1, street2] = getStreetLines(address.street); - const formattedAddress = formatPiece(street1) + formatPiece(street2) + formatPiece(address.city) + formatPiece(address.state) + formatPiece(address.zip) + formatPiece(address.country); - - // Remove the last comma of the address - return formattedAddress.trim().replace(/,$/, ''); -} - -/** - * @param {Object} personalDetail - details object - * @returns {String | undefined} - The effective display name - */ -function getEffectiveDisplayName(personalDetail) { - if (personalDetail) { - return LocalePhoneNumber.formatPhoneNumber(personalDetail.login) || personalDetail.displayName; - } - - return undefined; -} - -export { - getDisplayNameOrDefault, - getPersonalDetailsByIDs, - getAccountIDsByLogins, - getLoginsByAccountIDs, - getNewPersonalDetailsOnyxData, - getFormattedAddress, - getFormattedStreet, - getStreetLines, - getEffectiveDisplayName, -}; diff --git a/src/libs/PersonalDetailsUtils.ts b/src/libs/PersonalDetailsUtils.ts new file mode 100644 index 000000000000..8bb4ac0aea3e --- /dev/null +++ b/src/libs/PersonalDetailsUtils.ts @@ -0,0 +1,211 @@ +import Onyx, {OnyxEntry} from 'react-native-onyx'; +import ONYXKEYS from '@src/ONYXKEYS'; +import * as OnyxTypes from '@src/types/onyx'; +import {PersonalDetails, PersonalDetailsList} from '@src/types/onyx'; +import * as LocalePhoneNumber from './LocalePhoneNumber'; +import * as Localize from './Localize'; +import * as UserUtils from './UserUtils'; + +let personalDetails: Array = []; +let allPersonalDetails: OnyxEntry = {}; +Onyx.connect({ + key: ONYXKEYS.PERSONAL_DETAILS_LIST, + callback: (val) => { + personalDetails = Object.values(val ?? {}); + allPersonalDetails = val; + }, +}); + +/** + * @param [defaultValue] optional default display name value + */ +function getDisplayNameOrDefault(displayName?: string, defaultValue = ''): string { + return displayName ?? defaultValue ?? Localize.translateLocal('common.hidden'); +} + +/** + * Given a list of account IDs (as number) it will return an array of personal details objects. + * @param accountIDs - Array of accountIDs + * @param currentUserAccountID + * @param shouldChangeUserDisplayName - It will replace the current user's personal detail object's displayName with 'You'. + * @returns - Array of personal detail objects + */ +function getPersonalDetailsByIDs(accountIDs: number[], currentUserAccountID: number, shouldChangeUserDisplayName = false): OnyxTypes.PersonalDetails[] { + const result: OnyxTypes.PersonalDetails[] = accountIDs + .filter((accountID) => !!allPersonalDetails?.[accountID]) + .map((accountID) => { + const detail = (allPersonalDetails?.[accountID] ?? {}) as OnyxTypes.PersonalDetails; + + if (shouldChangeUserDisplayName && currentUserAccountID === detail.accountID) { + return { + ...detail, + displayName: Localize.translateLocal('common.you'), + }; + } + + return detail; + }); + + return result; +} + +/** + * Given a list of logins, find the associated personal detail and return related accountIDs. + * + * @param logins Array of user logins + * @returns Array of accountIDs according to passed logins + */ +function getAccountIDsByLogins(logins: string[]): number[] { + return logins.reduce((foundAccountIDs, login) => { + const currentDetail = personalDetails.find((detail) => detail?.login === login); + if (!currentDetail) { + // generate an account ID because in this case the detail is probably new, so we don't have a real accountID yet + foundAccountIDs.push(UserUtils.generateAccountID(login)); + } else { + foundAccountIDs.push(Number(currentDetail.accountID)); + } + return foundAccountIDs; + }, []); +} + +/** + * Given a list of accountIDs, find the associated personal detail and return related logins. + * + * @param accountIDs Array of user accountIDs + * @returns Array of logins according to passed accountIDs + */ +function getLoginsByAccountIDs(accountIDs: number[]): string[] { + return accountIDs.reduce((foundLogins: string[], accountID) => { + const currentDetail: Partial = personalDetails.find((detail) => Number(detail?.accountID) === Number(accountID)) ?? {}; + if (currentDetail.login) { + foundLogins.push(currentDetail.login); + } + return foundLogins; + }, []); +} + +/** + * Given a list of logins and accountIDs, return Onyx data for users with no existing personal details stored + * + * @param logins Array of user logins + * @param accountIDs Array of user accountIDs + * @returns Object with optimisticData, successData and failureData (object of personal details objects) + */ +function getNewPersonalDetailsOnyxData(logins: string[], accountIDs: number[]) { + const optimisticData: PersonalDetailsList = {}; + const successData: PersonalDetailsList = {}; + const failureData: PersonalDetailsList = {}; + + logins.forEach((login, index) => { + const accountID = accountIDs[index]; + + if (allPersonalDetails && Object.keys(allPersonalDetails?.[accountID] ?? {}).length === 0) { + optimisticData[accountID] = { + login, + accountID, + avatar: UserUtils.getDefaultAvatarURL(accountID), + displayName: LocalePhoneNumber.formatPhoneNumber(login), + }; + + /** + * Cleanup the optimistic user to ensure it does not permanently persist. + * This is done to prevent duplicate entries (upon success) since the BE will return other personal details with the correct account IDs. + */ + successData[accountID] = null; + } + }); + + return { + optimisticData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, + value: optimisticData, + }, + ], + successData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, + value: successData, + }, + ], + failureData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, + value: failureData, + }, + ], + }; +} + +/** + * Applies common formatting to each piece of an address + * + * @param piece - address piece to format + * @returns - formatted piece + */ +function formatPiece(piece?: string): string { + return piece ? `${piece}, ` : ''; +} + +/** + * + * @param street1 - street line 1 + * @param street2 - street line 2 + * @returns formatted street + */ +function getFormattedStreet(street1 = '', street2 = '') { + return `${street1}\n${street2}`; +} + +/** + * + * @param - formatted address + * @returns [street1, street2] + */ +function getStreetLines(street = '') { + const streets = street.split('\n'); + return [streets[0], streets[1]]; +} + +/** + * Formats an address object into an easily readable string + * + * @param privatePersonalDetails - details object + * @returns - formatted address + */ +function getFormattedAddress(privatePersonalDetails: OnyxTypes.PrivatePersonalDetails): string { + const {address} = privatePersonalDetails; + const [street1, street2] = getStreetLines(address?.street); + const formattedAddress = + formatPiece(street1) + formatPiece(street2) + formatPiece(address?.city) + formatPiece(address?.state) + formatPiece(address?.zip) + formatPiece(address?.country); + + // Remove the last comma of the address + return formattedAddress.trim().replace(/,$/, ''); +} + +/** + * @param personalDetail - details object + * @returns - The effective display name + */ +function getEffectiveDisplayName(personalDetail?: PersonalDetails): string | undefined { + if (personalDetail) { + return LocalePhoneNumber.formatPhoneNumber(personalDetail?.login ?? '') || personalDetail.displayName; + } + + return undefined; +} + +export { + getDisplayNameOrDefault, + getPersonalDetailsByIDs, + getAccountIDsByLogins, + getLoginsByAccountIDs, + getNewPersonalDetailsOnyxData, + getFormattedAddress, + getFormattedStreet, + getStreetLines, + getEffectiveDisplayName, +}; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 24e795919649..fce158c309b8 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -1458,12 +1458,12 @@ function getDisplayNamesWithTooltips( return personalDetailsListArray .map((user) => { - const accountID = Number(user.accountID); + const accountID = Number(user?.accountID); // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - const displayName = getDisplayNameForParticipant(accountID, isMultipleParticipantReport, shouldFallbackToHidden) || user.login || ''; + const displayName = getDisplayNameForParticipant(accountID, isMultipleParticipantReport, shouldFallbackToHidden) || user?.login || ''; const avatar = UserUtils.getDefaultAvatar(accountID); - let pronouns = user.pronouns; + let pronouns = user?.pronouns ?? undefined; if (pronouns?.startsWith(CONST.PRONOUNS.PREFIX)) { const pronounTranslationKey = pronouns.replace(CONST.PRONOUNS.PREFIX, ''); pronouns = Localize.translateLocal(`pronouns.${pronounTranslationKey}` as TranslationPaths); @@ -1472,7 +1472,7 @@ function getDisplayNamesWithTooltips( return { displayName, avatar, - login: user.login ?? '', + login: user?.login ?? '', accountID, pronouns, }; diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index 1813d4f0a795..4744426ecfd3 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -344,7 +344,7 @@ function getOptionData( case CONST.REPORT.ARCHIVE_REASON.POLICY_DELETED: { lastMessageText = Localize.translate(preferredLocale, `reportArchiveReasons.${archiveReason}`, { policyName: ReportUtils.getPolicyName(report, false, policy), - displayName: PersonalDetailsUtils.getDisplayNameOrDefault(lastActorDetails, 'displayName'), + displayName: PersonalDetailsUtils.getDisplayNameOrDefault(lastActorDetails?.displayName), }); break; } diff --git a/src/libs/UserUtils.ts b/src/libs/UserUtils.ts index e95b62cc2437..0fad9c2c2a75 100644 --- a/src/libs/UserUtils.ts +++ b/src/libs/UserUtils.ts @@ -7,7 +7,7 @@ import * as defaultAvatars from '@components/Icon/DefaultAvatars'; import {ConciergeAvatar, FallbackAvatar} from '@components/Icon/Expensicons'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import {PersonalDetails} from '@src/types/onyx'; +import {PersonalDetailsList} from '@src/types/onyx'; import Login from '@src/types/onyx/Login'; import hashCode from './hashCode'; @@ -17,7 +17,7 @@ type AvatarSource = React.FC | string; type LoginListIndicator = ValueOf | ''; -let allPersonalDetails: OnyxEntry>; +let allPersonalDetails: OnyxEntry; Onyx.connect({ key: ONYXKEYS.PERSONAL_DETAILS_LIST, callback: (val) => (allPersonalDetails = _.isEmpty(val) ? {} : val), diff --git a/src/pages/ReportParticipantsPage.js b/src/pages/ReportParticipantsPage.js index 4ec31b29f914..e04ffbb352fc 100755 --- a/src/pages/ReportParticipantsPage.js +++ b/src/pages/ReportParticipantsPage.js @@ -60,7 +60,7 @@ const getAllParticipants = (report, personalDetails, translate) => .map((accountID, index) => { const userPersonalDetail = lodashGet(personalDetails, accountID, {displayName: personalDetails.displayName || translate('common.hidden'), avatar: ''}); const userLogin = LocalePhoneNumber.formatPhoneNumber(userPersonalDetail.login || '') || translate('common.hidden'); - const displayName = PersonalDetailsUtils.getDisplayNameOrDefault(userPersonalDetail, 'displayName'); + const displayName = PersonalDetailsUtils.getDisplayNameOrDefault(userPersonalDetail.displayName); return { alternateText: userLogin, diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index d4731d3b929b..e14879a4d0ce 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -374,7 +374,7 @@ function ReportActionItem(props) { ); } else if (props.action.actionName === CONST.REPORT.ACTIONS.TYPE.REIMBURSEMENTQUEUED) { - const submitterDisplayName = PersonalDetailsUtils.getDisplayNameOrDefault(personalDetails, [props.report.ownerAccountID, 'displayName']); + const submitterDisplayName = PersonalDetailsUtils.getDisplayNameOrDefault(lodashGet(personalDetails, [props.report.ownerAccountID, 'displayName'])); const paymentType = lodashGet(props.action, 'originalMessage.paymentType', ''); const isSubmitterOfUnsettledReport = ReportUtils.isCurrentUserSubmitter(props.report.reportID) && !ReportUtils.isSettled(props.report.reportID); @@ -422,7 +422,7 @@ function ReportActionItem(props) { ); } else if (props.action.actionName === CONST.REPORT.ACTIONS.TYPE.REIMBURSEMENTDEQUEUED) { - const submitterDisplayName = PersonalDetailsUtils.getDisplayNameOrDefault(personalDetails, [props.report.ownerAccountID, 'displayName']); + const submitterDisplayName = PersonalDetailsUtils.getDisplayNameOrDefault(lodashGet(personalDetails, [props.report.ownerAccountID, 'displayName'])); const amount = CurrencyUtils.convertToDisplayString(props.report.total, props.report.currency); children = ; diff --git a/src/types/onyx/PersonalDetails.ts b/src/types/onyx/PersonalDetails.ts index bd2599fee0ca..9f613cbf4f1e 100644 --- a/src/types/onyx/PersonalDetails.ts +++ b/src/types/onyx/PersonalDetails.ts @@ -73,7 +73,7 @@ type PersonalDetails = { status?: string; }; -type PersonalDetailsList = Record; +type PersonalDetailsList = Record; export default PersonalDetails; diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts index 110bdb024a8c..9967f49fd377 100644 --- a/src/types/onyx/index.ts +++ b/src/types/onyx/index.ts @@ -57,7 +57,6 @@ import WalletTransfer from './WalletTransfer'; export type { Account, - UserLocation, AccountData, AddDebitCardForm, BankAccount, @@ -89,16 +88,16 @@ export type { PersonalDetailsList, PlaidData, Policy, - PolicyCategory, PolicyCategories, + PolicyCategory, PolicyMember, PolicyMembers, PolicyTag, PolicyTags, PrivatePersonalDetails, + RecentWaypoint, RecentlyUsedCategories, RecentlyUsedTags, - RecentWaypoint, ReimbursementAccount, ReimbursementAccountDraft, Report, @@ -116,6 +115,7 @@ export type { Transaction, TransactionViolation, User, + UserLocation, UserWallet, ViolationName, WalletAdditionalDetails,