Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
9facbae
Update delete workspace error message
jnowakow Jun 8, 2026
8ae1a01
Fix eslint
jnowakow Jun 8, 2026
065411a
Merge branch 'main' into jnowakow/update-delete-workspace-error-message
jnowakow Jun 8, 2026
700097b
Adjust to main
jnowakow Jun 8, 2026
0ff99a4
close modal when using link
jnowakow Jun 8, 2026
f4d8790
Explain why eslint-disable is needed
jnowakow Jun 12, 2026
74c13dd
Conditionally assign error message
jnowakow Jun 12, 2026
53efab6
conditionally assign html error
jnowakow Jun 12, 2026
0653121
add translations
jnowakow Jun 12, 2026
18939ab
fix lint
jnowakow Jun 12, 2026
21fa9b4
Fix narrow screen
staszekscp Jun 17, 2026
bbd55a8
Merge remote-tracking branch 'origin/main' into jnowakow/update-delet…
staszekscp Jun 17, 2026
bfb8b66
Merge remote-tracking branch 'origin/main' into jnowakow/update-delet…
staszekscp Jun 18, 2026
689536c
Finish working version
staszekscp Jun 18, 2026
3abb92a
Fix React import
staszekscp Jun 18, 2026
da15d0b
Fix Knip
staszekscp Jun 18, 2026
c06f23d
Fix linter
staszekscp Jun 19, 2026
8d3d912
Switch to useConfirmModal
staszekscp Jun 19, 2026
765eb1a
Switch to useConfirmModal WorkspaceOverviewPage
staszekscp Jun 19, 2026
5e31a98
Apply review changes
staszekscp Jun 22, 2026
01b9991
Merge remote-tracking branch 'origin/main' into jnowakow/update-delet…
staszekscp Jun 22, 2026
fdf73b3
Fix prettier
staszekscp Jun 22, 2026
c8dd6ca
Fix ESLint
staszekscp Jun 22, 2026
b603369
Apply changes
staszekscp Jun 22, 2026
f5c663d
Fix
staszekscp Jun 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 29 additions & 2 deletions src/components/DotIndicatorMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@ import type {TranslationKeyError} from '@src/types/onyx/OnyxCommon';
import type {ReceiptError} from '@src/types/onyx/Transaction';
import Button from './Button';
import Icon from './Icon';
import RenderHTML from './RenderHTML';
import Text from './Text';

const HTML_TAG_PATTERN = /<\/?[a-z][^>]*>/i;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@jnowakow Could you please add this to CONST

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@staszekscp Could you please address this one?


type DotIndicatorMessageProps = {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is the change in this file necessary? I checked with AI, and it said:

Neither WorkspaceOverviewPage nor WorkspacesListPage use DotIndicatorMessage for this error — they both use the confirmation modal. The DotIndicatorMessage change appears to be dead code for the purpose of this PR.

Could you clarify whether this change is actually needed?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Without it the error in the table would not render correctly
image

/**
* In most cases this should just be errors from onxyData
Expand Down Expand Up @@ -74,7 +77,31 @@ function DotIndicatorMessage({messages = {}, style, type, textStyles, dismissErr
}

const displayMessage = isTranslationKeyError(message) ? translate(message.translationKey) : message;
const formattedMessage = typeof displayMessage === 'string' ? Str.htmlDecode(displayMessage) : displayMessage;

if (typeof displayMessage !== 'string') {
return (
<Text
key={index}
style={[StyleUtils.getDotIndicatorTextStyles(isErrorMessage), textStyles, isTextSelectable ? styles.userSelectText : styles.userSelectNone]}
accessibilityRole={isErrorMessage ? CONST.ROLE.ALERT : undefined}
accessibilityLiveRegion={isErrorMessage ? 'assertive' : undefined}
>
{displayMessage}
</Text>
);
}

if (HTML_TAG_PATTERN.test(displayMessage)) {
const html = isErrorMessage ? `<rbr>${displayMessage}</rbr>` : `<muted-text-label>${displayMessage}</muted-text-label>`;

return (
<RenderHTML
key={index}
html={html}
isSelectable={isTextSelectable}
/>
);
}

return (
<Text
Expand All @@ -83,7 +110,7 @@ function DotIndicatorMessage({messages = {}, style, type, textStyles, dismissErr
accessibilityRole={isErrorMessage ? CONST.ROLE.ALERT : undefined}
accessibilityLiveRegion={isErrorMessage ? 'assertive' : undefined}
>
{formattedMessage}
{Str.htmlDecode(displayMessage)}
</Text>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {hasSeenTourSelector} from '@selectors/Onboarding';
import React from 'react';
import type {StyleProp, TextStyle} from 'react-native';
import type {CustomRendererProps, TPhrasing, TText} from 'react-native-render-html';
import {TNodeChildrenRenderer} from 'react-native-render-html';
import type {CustomRendererProps, RenderersProps, TPhrasing, TText} from 'react-native-render-html';
import {TNodeChildrenRenderer, useRendererProps} from 'react-native-render-html';
import * as HTMLEngineUtils from '@components/HTMLEngineProvider/htmlEngineUtils';
import Text from '@components/Text';
import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
Expand All @@ -15,18 +15,30 @@ import ONYXKEYS from '@src/ONYXKEYS';

type ConciergeLinkRendererProps = CustomRendererProps<TText | TPhrasing>;

type ConciergeLinkRendererConfig = {
onPress?: () => void;
};

type ConciergeLinkRenderersProps = RenderersProps & {
// Custom HTML renderer keys must use hyphenated tag names per react-native-render-html API
/* eslint-disable @typescript-eslint/naming-convention */
'concierge-link': ConciergeLinkRendererConfig;
};

function ConciergeLinkRenderer({tnode, style}: ConciergeLinkRendererProps) {
const styles = useThemeStyles();
const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID);
const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED);
const [betas] = useOnyx(ONYXKEYS.BETAS);
const [isSelfTourViewed] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: hasSeenTourSelector});
const {accountID: currentUserAccountID} = useCurrentUserPersonalDetails();
const {onPress: onPressFromProps} = useRendererProps<ConciergeLinkRenderersProps, 'concierge-link'>('concierge-link') ?? {};

/**
* Simple wrapper to create a stable reference without passing event args to navigation function.
*/
const navigateToConciergeChat = () => {
onPressFromProps?.();
navigateToConciergeChatAction(conciergeReportID, introSelected, currentUserAccountID, isSelfTourViewed, betas, false);
};

Expand Down
17 changes: 14 additions & 3 deletions src/components/RenderHTML.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import useHasTextAncestor from '@hooks/useHasTextAncestor';
import useWindowDimensions from '@hooks/useWindowDimensions';
import Parser from '@libs/Parser';
import BulletItemRenderer from './HTMLEngineProvider/HTMLRenderers/BulletItemRenderer';
import ConciergeLinkRenderer from './HTMLEngineProvider/HTMLRenderers/ConciergeLinkRenderer';
import OLRenderer from './HTMLEngineProvider/HTMLRenderers/OLRenderer';
import SparklesIconRenderer from './HTMLEngineProvider/HTMLRenderers/SparklesIconRenderer';
import ULRenderer from './HTMLEngineProvider/HTMLRenderers/ULRenderer';

type LinkPressHandler = NonNullable<RenderersProps['a']>['onPress'];
type ConciergeLinkPressHandler = () => void;

// Matches &amp;#91; (→ "[") and &amp;#93; (→ "]"). Index 7 is the distinguishing digit ('1' vs '3').
const RE_BRACKET_ESCAPE = /&amp;#9[13];/g;
Expand All @@ -25,6 +27,9 @@ type RenderHTMLProps = {
/** Callback to handle link press */
onLinkPress?: LinkPressHandler;

/** Callback to handle concierge-link press */
onConciergeLinkPress?: ConciergeLinkPressHandler;

/** Whether the rendered text should be selectable */
isSelectable?: boolean;
};
Expand All @@ -33,7 +38,7 @@ type RenderHTMLProps = {
// Configuration for RenderHTML is handled in a top-level component providing
// context to RenderHTMLSource components. See https://git.io/JRcZb
// The provider is available at src/components/HTMLEngineProvider/
function RenderHTML({html: htmlParam, onLinkPress, isSelectable}: RenderHTMLProps) {
function RenderHTML({html: htmlParam, onLinkPress, onConciergeLinkPress, isSelectable}: RenderHTMLProps) {
const hasTextAncestor = useHasTextAncestor();
if (__DEV__ && hasTextAncestor) {
throw new Error('RenderHTML must not be rendered inside a <Text> component, as it will break the layout on iOS. Render it as a sibling instead.');
Expand All @@ -57,12 +62,18 @@ function RenderHTML({html: htmlParam, onLinkPress, isSelectable}: RenderHTMLProp
a: {
onPress: onLinkPress,
},
// Custom HTML renderer keys must use hyphenated tag names per react-native-render-html API
/* eslint-disable @typescript-eslint/naming-convention */
Comment thread
jnowakow marked this conversation as resolved.
'concierge-link': {
onPress: onConciergeLinkPress,
},
};
}, [onLinkPress]);
}, [onLinkPress, onConciergeLinkPress]);

const renderers = {
/* eslint-disable @typescript-eslint/naming-convention */
'bullet-item': BulletItemRenderer,
'concierge-link': ConciergeLinkRenderer,
'sparkles-icon': SparklesIconRenderer,
ol: OLRenderer,
ul: ULRenderer,
Expand All @@ -75,7 +86,7 @@ function RenderHTML({html: htmlParam, onLinkPress, isSelectable}: RenderHTMLProp
/>
);

return onLinkPress ? (
return onLinkPress || onConciergeLinkPress ? (
<RenderHTMLConfigProvider
defaultTextProps={{selectable: isSelectable ?? true, allowFontScaling: false}}
renderersProps={renderersProps}
Expand Down
2 changes: 1 addition & 1 deletion src/languages/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4424,7 +4424,7 @@ ${amount} für ${merchant} – ${date}`,
defaultNote: `Belege, die an ${CONST.EMAIL.RECEIPTS} gesendet werden, erscheinen in diesem Workspace.`,
deleteConfirmation: 'Möchten Sie diesen Workspace wirklich löschen?',
deleteWithCardsConfirmation: 'Möchtest du diesen Workspace wirklich löschen? Dadurch werden alle Kartenfeeds und zugewiesenen Karten entfernt.',
deleteOpenExpensifyCardsError: 'Ihr Unternehmen hat noch aktive Expensify Cards.',
deleteOpenExpensifyCardsError: 'Ihre Firma hat noch Expensify Karten. Bitte <concierge-link>wenden Sie sich an Concierge</concierge-link>, um sie zu entfernen.',
outstandingBalanceWarning:
'Sie haben einen offenen Saldo, der beglichen werden muss, bevor Sie Ihren letzten Workspace löschen können. Bitte gehen Sie zu Ihren Abonnementeinstellungen, um die Zahlung abzuschließen.',
settleBalance: 'Zu Abo wechseln',
Expand Down
2 changes: 1 addition & 1 deletion src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4516,7 +4516,7 @@ const translations = {
defaultNote: `Receipts sent to ${CONST.EMAIL.RECEIPTS} will appear in this workspace.`,
deleteConfirmation: 'Are you sure you want to delete this workspace?',
deleteWithCardsConfirmation: 'Are you sure you want to delete this workspace? This will remove all card feeds and assigned cards.',
deleteOpenExpensifyCardsError: 'Your company still has open Expensify Cards.',
deleteOpenExpensifyCardsError: 'Your company still has Expensify Cards. Please <concierge-link>reach out to Concierge</concierge-link> to remove them.',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Missing translations in other languages. I think we can trigger translations workflow for this

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Right! I'll ask someone on slack to trigger the translation script

outstandingBalanceWarning:
'You have an outstanding balance that must be settled before deleting your last workspace. Please go to your subscription settings to resolve the payment.',
settleBalance: 'Go to subscription',
Expand Down
2 changes: 1 addition & 1 deletion src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4299,7 +4299,7 @@ ${amount} para ${merchant} - ${date}`,
defaultNote: `Los recibos enviados a ${CONST.EMAIL.RECEIPTS} aparecerán en este espacio de trabajo.`,
deleteConfirmation: '¿Estás seguro de que quieres eliminar este espacio de trabajo?',
deleteWithCardsConfirmation: '¿Estás seguro de que quieres eliminar este espacio de trabajo? Se eliminarán todos los datos de las tarjetas y las tarjetas asignadas.',
deleteOpenExpensifyCardsError: 'Su empresa todavía tiene tarjetas Expensify activas.',
deleteOpenExpensifyCardsError: 'Tu empresa todavía tiene Tarjetas Expensify. Por favor, <concierge-link>contacta con Concierge</concierge-link> para eliminarlas.',
outstandingBalanceWarning:
'Tienes un saldo pendiente que debe liquidarse antes de eliminar tu último espacio de trabajo. Por favor, ve a la configuración de tu suscripción para resolver el pago.',
settleBalance: 'Ir a Suscripción',
Expand Down
2 changes: 1 addition & 1 deletion src/languages/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4435,7 +4435,7 @@ ${amount} pour ${merchant} - ${date}`,
defaultNote: `Les reçus envoyés à ${CONST.EMAIL.RECEIPTS} apparaîtront dans cet espace de travail.`,
deleteConfirmation: 'Voulez-vous vraiment supprimer cet espace de travail ?',
deleteWithCardsConfirmation: 'Voulez-vous vraiment supprimer cet espace de travail ? Cela supprimera tous les flux de cartes et les cartes assignées.',
deleteOpenExpensifyCardsError: 'Votre entreprise a encore des cartes Expensify actives.',
deleteOpenExpensifyCardsError: 'Votre entreprise a encore des Cartes Expensify. Veuillez <concierge-link>contacter Concierge</concierge-link> pour les supprimer.',
outstandingBalanceWarning:
'Vous avez un solde impayé qui doit être réglé avant de supprimer votre dernier espace de travail. Veuillez accéder à vos paramètres d’abonnement pour résoudre le paiement.',
settleBalance: 'Aller à l’abonnement',
Expand Down
2 changes: 1 addition & 1 deletion src/languages/it.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4410,7 +4410,7 @@ ${amount} per ${merchant} - ${date}`,
defaultNote: `Le ricevute inviate a ${CONST.EMAIL.RECEIPTS} verranno visualizzate in questo workspace.`,
deleteConfirmation: 'Sei sicuro di voler eliminare questo spazio di lavoro?',
deleteWithCardsConfirmation: 'Sei sicuro di voler eliminare questo spazio di lavoro? Questa azione rimuoverà tutti i feed delle carte e le carte assegnate.',
deleteOpenExpensifyCardsError: 'La tua azienda ha ancora carte Expensify attive.',
deleteOpenExpensifyCardsError: 'La tua azienda ha ancora delle Carte Expensify. Per favore, <concierge-link>contatta Concierge</concierge-link> per rimuoverle.',
outstandingBalanceWarning:
'Hai un saldo in sospeso che deve essere saldato prima di eliminare il tuo ultimo workspace. Vai alle impostazioni dell’abbonamento per risolvere il pagamento.',
settleBalance: 'Vai all’abbonamento',
Expand Down
2 changes: 1 addition & 1 deletion src/languages/ja.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4381,7 +4381,7 @@ ${integrationName === CONST.ONBOARDING_ACCOUNTING_MAPPING.other ? 'あなたの'
defaultNote: `${CONST.EMAIL.RECEIPTS} に送信されたレシートは、このワークスペースに表示されます。`,
deleteConfirmation: 'このワークスペースを削除してもよろしいですか?',
deleteWithCardsConfirmation: 'このワークスペースを削除してもよろしいですか? すべてのカードフィードと割り当て済みカードが削除されます。',
deleteOpenExpensifyCardsError: 'あなたの会社にはまだ有効なExpensifyカードがあります。',
deleteOpenExpensifyCardsError: '御社にはまだ Expensify カードが残っています。削除するには、<concierge-link>Concierge までお問い合わせください</concierge-link>。',
outstandingBalanceWarning: '最後のワークスペースを削除する前に精算する必要がある未払残高があります。支払いを解決するには、サブスクリプション設定に移動してください。',
settleBalance: 'サブスクリプションに移動',
unavailable: '利用できないワークスペース',
Expand Down
2 changes: 1 addition & 1 deletion src/languages/nl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4405,7 +4405,7 @@ ${amount} voor ${merchant} - ${date}`,
defaultNote: `Bonnetjes die naar ${CONST.EMAIL.RECEIPTS} worden gestuurd, verschijnen in deze workspace.`,
deleteConfirmation: 'Weet je zeker dat je deze werkruimte wilt verwijderen?',
deleteWithCardsConfirmation: 'Weet je zeker dat je deze werkruimte wilt verwijderen? Hiermee worden alle kaartfeeds en toegewezen kaarten verwijderd.',
deleteOpenExpensifyCardsError: 'Uw bedrijf heeft nog actieve Expensify Cards.',
deleteOpenExpensifyCardsError: 'Je bedrijf heeft nog Expensify Kaarten. <concierge-link>Neem contact op met Concierge</concierge-link> om ze te verwijderen.',
outstandingBalanceWarning:
'Je hebt een openstaand saldo dat moet worden vereffend voordat je je laatste werkruimte kunt verwijderen. Ga naar je abonnementsinstellingen om de betaling af te ronden.',
settleBalance: 'Ga naar abonnement',
Expand Down
2 changes: 1 addition & 1 deletion src/languages/pl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4398,7 +4398,7 @@ ${amount} dla ${merchant} - ${date}`,
defaultNote: `Paragony wysłane na ${CONST.EMAIL.RECEIPTS} pojawią się w tym obszarze roboczym.`,
deleteConfirmation: 'Czy na pewno chcesz usunąć tę przestrzeń roboczą?',
deleteWithCardsConfirmation: 'Na pewno chcesz usunąć tę przestrzeń roboczą? Spowoduje to usunięcie wszystkich źródeł kart i przypisanych kart.',
deleteOpenExpensifyCardsError: 'Twoja firma nadal ma aktywne karty Expensify.',
deleteOpenExpensifyCardsError: 'Twoja firma wciąż ma Karty Expensify. Prosimy, <concierge-link>skontaktuj się z Concierge</concierge-link>, aby je usunąć.',
outstandingBalanceWarning:
'Masz zaległe saldo, które musi zostać uregulowane przed usunięciem ostatniego miejsca pracy. Przejdź do ustawień subskrypcji, aby uregulować płatność.',
settleBalance: 'Przejdź do subskrypcji',
Expand Down
2 changes: 1 addition & 1 deletion src/languages/pt-BR.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4395,7 +4395,7 @@ ${amount} para ${merchant} - ${date}`,
defaultNote: `Recibos enviados para ${CONST.EMAIL.RECEIPTS} aparecerão neste workspace.`,
deleteConfirmation: 'Tem certeza de que deseja excluir este workspace?',
deleteWithCardsConfirmation: 'Tem certeza de que deseja excluir este workspace? Isso removerá todos os feeds de cartão e cartões atribuídos.',
deleteOpenExpensifyCardsError: 'Sua empresa ainda possui cartões Expensify ativos.',
deleteOpenExpensifyCardsError: 'Sua empresa ainda tem Cartões Expensify. Por favor, <concierge-link>fale com o Concierge</concierge-link> para removê-los.',
outstandingBalanceWarning:
'Você tem um saldo pendente que precisa ser quitado antes de excluir seu último espaço de trabalho. Acesse as configurações de assinatura para resolver o pagamento.',
settleBalance: 'Ir para a assinatura',
Expand Down
2 changes: 1 addition & 1 deletion src/languages/zh-hans.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4294,7 +4294,7 @@ ${amount},商户:${merchant} - 日期:${date}`,
defaultNote: `发送到 ${CONST.EMAIL.RECEIPTS} 的收据将显示在此工作区中。`,
deleteConfirmation: '确定要删除此工作区吗?',
deleteWithCardsConfirmation: '确定要删除此工作区吗?这将移除所有卡片数据源和已分配的卡片。',
deleteOpenExpensifyCardsError: '您的公司仍有未关闭的 Expensify 卡。',
deleteOpenExpensifyCardsError: '您的公司仍在使用 Expensify 卡。请<concierge-link>联系 Concierge</concierge-link>以停用它们。',
outstandingBalanceWarning: '您有一笔未结清的余额,必须在删除最后一个工作区之前结清。请前往订阅设置以解决付款问题。',
settleBalance: '前往订阅',
unavailable: '工作区不可用',
Expand Down
20 changes: 8 additions & 12 deletions src/libs/actions/Policy/Policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,13 @@ function deleteWorkspace(params: DeleteWorkspaceActionParams) {
const filteredPolicies = Object.values(policies ?? {}).filter((p): p is Policy => p?.id !== policyID);
const workspaceAccountID = policy?.policyAccountID;

if (hasDeleteWorkspaceExpensifyCardsError) {
Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {
errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('workspace.common.deleteOpenExpensifyCardsError'),
});
return;
}

const optimisticData: Array<
OnyxUpdate<
| typeof ONYXKEYS.COLLECTION.POLICY
Expand All @@ -430,7 +437,7 @@ function deleteWorkspace(params: DeleteWorkspaceActionParams) {
value: {
avatarURL: '',
pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE,
errors: hasDeleteWorkspaceExpensifyCardsError ? ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('workspace.common.deleteOpenExpensifyCardsError') : null,
errors: null,
},
},
{
Expand Down Expand Up @@ -614,16 +621,6 @@ function deleteWorkspace(params: DeleteWorkspaceActionParams) {
},
});

if (hasDeleteWorkspaceExpensifyCardsError) {
failureData.push({
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
value: {
errors: null,
},
});
}

reportIDToOptimisticCloseReportActionID[reportID] = optimisticClosedReportAction.reportActionID;

for (const transactionViolationKey of Object.keys(transactionViolations ?? {})) {
Expand Down Expand Up @@ -7547,7 +7544,6 @@ export {
deleteWorkspace,
updateAddress,
updateLastAccessedWorkspace,
clearDeleteWorkspaceError,
dismissWorkspaceError,
setWorkspaceDefaultSpendCategory,
getDisplayNameForWorkspace,
Expand Down
Loading
Loading