Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion src/languages/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -945,7 +945,7 @@ const translations: TranslationDeepObject<typeof en> = {
defaultSubtitle: 'Arbeitsbereich',
subtitle: ({policyName}: {policyName: string}) => `${policyName} > Unternehmenskarten`,
},
fixAccountingConnection: {
fixPolicyConnection: {
title: ({integrationName}: {integrationName: string}) => `${integrationName}-Verbindung reparieren`,
defaultSubtitle: 'Arbeitsbereich',
subtitle: ({policyName}: {policyName: string}) => `${policyName} > Buchhaltung`,
Expand Down
2 changes: 1 addition & 1 deletion src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -971,7 +971,7 @@ const translations = {
title: ({cardName}: {cardName?: string}) => (cardName ? `Fix ${cardName} personal card connection` : 'Fix personal card connection'),
subtitle: 'Wallet',
},
fixAccountingConnection: {
fixPolicyConnection: {
title: ({integrationName}: {integrationName: string}) => `Fix ${integrationName} connection`,
defaultSubtitle: 'Workspace',
subtitle: ({policyName}: {policyName: string}) => policyName,
Expand Down
2 changes: 1 addition & 1 deletion src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -882,7 +882,7 @@ const translations: TranslationDeepObject<typeof en> = {
title: ({cardName}: {cardName?: string}) => (cardName ? `Arreglar la conexión de la tarjeta personal de ${cardName}` : 'Arreglar la conexión de la tarjeta personal'),
subtitle: 'Billetera',
},
fixAccountingConnection: {
fixPolicyConnection: {
title: ({integrationName}: {integrationName: string}) => `Reconectar con ${integrationName}`,
defaultSubtitle: 'Espacio de trabajo',
subtitle: ({policyName}: {policyName: string}) => policyName,
Expand Down
2 changes: 1 addition & 1 deletion src/languages/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -948,7 +948,7 @@ const translations: TranslationDeepObject<typeof en> = {
defaultSubtitle: 'Espace de travail',
subtitle: ({policyName}: {policyName: string}) => `${policyName} > Cartes d'entreprise`,
},
fixAccountingConnection: {
fixPolicyConnection: {
title: ({integrationName}: {integrationName: string}) => `Corriger la connexion ${integrationName}`,
defaultSubtitle: 'Espace de travail',
subtitle: ({policyName}: {policyName: string}) => `${policyName} > Comptabilité`,
Expand Down
2 changes: 1 addition & 1 deletion src/languages/it.ts
Original file line number Diff line number Diff line change
Expand Up @@ -946,7 +946,7 @@ const translations: TranslationDeepObject<typeof en> = {
defaultSubtitle: 'Area di lavoro',
subtitle: ({policyName}: {policyName: string}) => `${policyName} > Carte aziendali`,
},
fixAccountingConnection: {
fixPolicyConnection: {
title: ({integrationName}: {integrationName: string}) => `Correggi connessione ${integrationName}`,
defaultSubtitle: 'Area di lavoro',
subtitle: ({policyName}: {policyName: string}) => `${policyName} > Contabilità`,
Expand Down
2 changes: 1 addition & 1 deletion src/languages/ja.ts
Original file line number Diff line number Diff line change
Expand Up @@ -932,7 +932,7 @@ const translations: TranslationDeepObject<typeof en> = {
defaultSubtitle: 'ワークスペース',
subtitle: ({policyName}: {policyName: string}) => `${policyName} > 会社カード`,
},
fixAccountingConnection: {
fixPolicyConnection: {
title: ({integrationName}: {integrationName: string}) => `${integrationName} 接続を修正`,
defaultSubtitle: 'ワークスペース',
subtitle: ({policyName}: {policyName: string}) => `${policyName} > 会計`,
Expand Down
2 changes: 1 addition & 1 deletion src/languages/nl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -944,7 +944,7 @@ const translations: TranslationDeepObject<typeof en> = {
defaultSubtitle: 'Werkruimte',
subtitle: ({policyName}: {policyName: string}) => `${policyName} > Bedrijfspassen`,
},
fixAccountingConnection: {
fixPolicyConnection: {
title: ({integrationName}: {integrationName: string}) => `Verbinding met ${integrationName} repareren`,
defaultSubtitle: 'Werkruimte',
subtitle: ({policyName}: {policyName: string}) => `${policyName} > Boekhouding`,
Expand Down
2 changes: 1 addition & 1 deletion src/languages/pl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -946,7 +946,7 @@ const translations: TranslationDeepObject<typeof en> = {
defaultSubtitle: 'Obszar roboczy',
subtitle: ({policyName}: {policyName: string}) => `${policyName} > Karty firmowe`,
},
fixAccountingConnection: {
fixPolicyConnection: {
title: ({integrationName}: {integrationName: string}) => `Napraw połączenie ${integrationName}`,
defaultSubtitle: 'Obszar roboczy',
subtitle: ({policyName}: {policyName: string}) => `${policyName} > Księgowość`,
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 @@ -944,7 +944,7 @@ const translations: TranslationDeepObject<typeof en> = {
defaultSubtitle: 'Espaço de trabalho',
subtitle: ({policyName}: {policyName: string}) => `${policyName} > Cartões corporativos`,
},
fixAccountingConnection: {
fixPolicyConnection: {
title: ({integrationName}: {integrationName: string}) => `Corrigir conexão com ${integrationName}`,
defaultSubtitle: 'Espaço de trabalho',
subtitle: ({policyName}: {policyName: string}) => `${policyName} > Contabilidade`,
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 @@ -916,7 +916,7 @@ const translations: TranslationDeepObject<typeof en> = {
subtitle: ({policyName}: {policyName: string}) => `${policyName} > 公司卡片`,
},
fixPersonalCardConnection: {title: ({cardName}: {cardName?: string}) => (cardName ? `修复 ${cardName} 个人卡连接` : '修复个人银行卡连接'), subtitle: '钱包'},
fixAccountingConnection: {
fixPolicyConnection: {
title: ({integrationName}: {integrationName: string}) => `修复 ${integrationName} 连接`,
defaultSubtitle: '工作区',
subtitle: ({policyName}: {policyName: string}) => `${policyName} > 会计`,
Expand Down
34 changes: 22 additions & 12 deletions src/pages/home/TimeSensitiveSection/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import useOnyx from '@hooks/useOnyx';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useThemeStyles from '@hooks/useThemeStyles';
import {hasSynchronizationErrorMessage, isConnectionInProgress} from '@libs/actions/connections';
import {getConnectedHRProvider} from '@libs/PolicyUtils';
import {isCurrentUserValidated} from '@libs/UserUtils';
import HomeSectionExpandToggle from '@pages/home/HomeSectionExpandToggle';
import CONST from '@src/CONST';
Expand All @@ -27,15 +28,15 @@ import useTimeSensitiveLockedBankAccount from './hooks/useTimeSensitiveLockedBan
import ActivateCard from './items/ActivateCard';
import AddPaymentCard from './items/AddPaymentCard';
import AddShippingAddress from './items/AddShippingAddress';
import FixAccountingConnection from './items/FixAccountingConnection';
import FixCompanyCardConnection from './items/FixCompanyCardConnection';
import FixFailedBilling from './items/FixFailedBilling';
import FixPersonalCardConnection from './items/FixPersonalCardConnection';
import FixPolicyConnection from './items/FixPolicyConnection';
import ReviewCardFraud from './items/ReviewCardFraud';
import UnlockBankAccount from './items/UnlockBankAccount';
import ValidateAccount from './items/ValidateAccount';

type BrokenAccountingConnection = {
type BrokenPolicyConnection = {
/** The policy ID associated with this connection */
policyID: string;

Expand All @@ -44,6 +45,9 @@ type BrokenAccountingConnection = {

/** The connection name that has an error */
connectionName: PolicyConnectionName;

/** Human-readable integration name (e.g. "QuickBooks Online", "Gusto", "BambooHR"). */
integrationName: string;
};

type BrokenCompanyCardConnection = {
Expand Down Expand Up @@ -97,8 +101,8 @@ function TimeSensitiveSection() {
// Get card feed errors for company card connections (Release 4)
const cardFeedErrors = useCardFeedErrors();

// Find policies with broken accounting connections (only for admins)
const brokenAccountingConnections: BrokenAccountingConnection[] = [];
// Find policies with broken connections (accounting + HR, only for admins)
const brokenPolicyConnections: BrokenPolicyConnection[] = [];
for (const policy of adminPolicies ?? []) {
const policyConnections = policy.connections;
if (!policyConnections) {
Expand All @@ -111,10 +115,15 @@ function TimeSensitiveSection() {

for (const connectionName of Object.keys(policyConnections) as ConnectionName[]) {
if (hasSynchronizationErrorMessage(policy, connectionName, isSyncInProgress)) {
brokenAccountingConnections.push({
const integrationName =
connectionName === CONST.POLICY.CONNECTIONS.NAME.MERGE_HR
? (getConnectedHRProvider(policy)?.displayName ?? CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[connectionName])
: CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[connectionName];
brokenPolicyConnections.push({
policyID: policy.id,
policyName: policy.name,
connectionName,
integrationName,
});
}
}
Expand Down Expand Up @@ -158,7 +167,7 @@ function TimeSensitiveSection() {

const hasBrokenCompanyCards = brokenCompanyCardConnections.length > 0;
const hasBrokenPersonalCards = brokenPersonalCardConnections.length > 0;
const hasBrokenAccountingConnections = brokenAccountingConnections.length > 0;
const hasBrokenPolicyConnections = brokenPolicyConnections.length > 0;
const isCurrentLoginValidated = isCurrentUserValidated(loginList, sessionEmail ?? login);
const shouldShowValidateAccount = isUserValidated === false && !isAnonymous && !isCurrentLoginValidated;

Expand All @@ -173,7 +182,7 @@ function TimeSensitiveSection() {
shouldShowAddPaymentCard ||
hasBrokenCompanyCards ||
hasBrokenPersonalCards ||
hasBrokenAccountingConnections ||
hasBrokenPolicyConnections ||
shouldShowAddShippingAddress ||
shouldShowActivateCard;

Expand All @@ -189,7 +198,7 @@ function TimeSensitiveSection() {
// 5. Broken bank connections (company cards)
// 6. Broken bank connections (personal cards)
// 7. Locked bank accounts (workspace VBAs and personal)
// 8. Broken accounting connections
// 8. Broken policy connections (accounting + HR)
// 9. Expensify card shipping
// 10. Expensify card activation
const items: React.ReactNode[] = [];
Expand Down Expand Up @@ -258,14 +267,15 @@ function TimeSensitiveSection() {
/>,
);
}
// Priority 8: Broken accounting connections
for (const connection of brokenAccountingConnections) {
// Priority 8: Broken policy connections (accounting + HR)
for (const connection of brokenPolicyConnections) {
items.push(
<FixAccountingConnection
key={`accounting-${connection.policyID}-${connection.connectionName}`}
<FixPolicyConnection
key={`policy-connection-${connection.policyID}-${connection.connectionName}`}
connectionName={connection.connectionName}
policyID={connection.policyID}
policyName={connection.policyName}
integrationName={connection.integrationName}
/>,
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import CONST from '@src/CONST';
import ROUTES from '@src/ROUTES';
import type {PolicyConnectionName} from '@src/types/onyx/Policy';

type FixAccountingConnectionProps = {
type FixPolicyConnectionProps = {
/** The connection name that has an error */
connectionName: PolicyConnectionName;

Expand All @@ -17,29 +17,34 @@ type FixAccountingConnectionProps = {

/** The policy name associated with this connection */
policyName: string;

/** Human-readable integration name to render (e.g. "QuickBooks Online", "Gusto", "BambooHR"). */
integrationName: string;
};

function FixAccountingConnection({connectionName, policyID, policyName}: FixAccountingConnectionProps) {
function FixPolicyConnection({connectionName, policyID, policyName, integrationName}: FixPolicyConnectionProps) {
const {translate} = useLocalize();
const icons = useMemoizedLazyExpensifyIcons(['Connect']);

const integrationName = CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[connectionName];
const subtitle = policyName
? translate('homePage.timeSensitiveSection.fixAccountingConnection.subtitle', {policyName})
: translate('homePage.timeSensitiveSection.fixAccountingConnection.defaultSubtitle');
? translate('homePage.timeSensitiveSection.fixPolicyConnection.subtitle', {policyName})
: translate('homePage.timeSensitiveSection.fixPolicyConnection.defaultSubtitle');

const isHRConnection = (CONST.POLICY.CONNECTIONS.HR_CONNECTION_NAMES as readonly PolicyConnectionName[]).includes(connectionName);
const fixRoute = isHRConnection ? ROUTES.WORKSPACE_HR.getRoute(policyID) : ROUTES.WORKSPACE_ACCOUNTING.getRoute(policyID);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Gate HR fix CTAs before routing to HR

When this renders for a broken HR connection, the CTA now always navigates to WORKSPACE_HR, but WorkspaceHRPage is blocked unless an HR beta is enabled (shouldBeBlocked) and the policy passes the control/admin access wrapper. TimeSensitiveSection only filters adminPolicies, so admins who can see this time-sensitive item without the HR beta/control access will land on the not-found page instead of somewhere they can fix the connection; either hide the HR item in those contexts or route to an accessible fallback.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

@mhawryluk mhawryluk May 27, 2026

Choose a reason for hiding this comment

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

I guess we could, but we don't check for a beta when navigating to the HR page from Members and Workflows pages when a provider is connected. so I think it's okay here too

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.

I think it's fine too, since a user can have a broken connection only if they have a beta


return (
<BaseWidgetItem
icon={icons.Connect}
iconBackgroundColor={colors.tangerine100}
iconFill={colors.tangerine500}
title={translate('homePage.timeSensitiveSection.fixAccountingConnection.title', {integrationName})}
title={translate('homePage.timeSensitiveSection.fixPolicyConnection.title', {integrationName})}
subtitle={subtitle}
ctaText={translate('homePage.timeSensitiveSection.ctaFix')}
onCtaPress={() => Navigation.navigate(ROUTES.WORKSPACE_ACCOUNTING.getRoute(policyID))}
onCtaPress={() => Navigation.navigate(fixRoute)}
buttonProps={{danger: true}}
/>
);
}

export default FixAccountingConnection;
export default FixPolicyConnection;
Loading