Skip to content
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,14 @@ export function JiraIntegrationCard(): React.JSX.Element {
}
checking={checking}
statusTone={connected ? 'connected' : 'attention'}
statusLabel={connected ? 'Connected' : 'Not connected'}
statusLabel={
connected
? translate('auto.components.settings.jira.integration.card.statusConnected', 'Connected')
: translate(
'auto.components.settings.jira.integration.card.statusNotConnected',
'Not connected'
)
}
actions={
!checking ? (
<Button
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,17 @@ export function LinearIntegrationCard(): React.JSX.Element {
}
checking={checking}
statusTone={connected ? 'connected' : 'attention'}
statusLabel={connected ? 'Connected' : 'Not connected'}
statusLabel={
connected
? translate(
'auto.components.settings.task.tracker.integration.cards.statusConnected',
'Connected'
)
: translate(
'auto.components.settings.task.tracker.integration.cards.statusNotConnected',
'Not connected'
)
}
actions={
!checking ? (
<Button
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Button } from '@/components/ui/button'
import { IntegrationCardDetails, IntegrationCardShell } from './integration-card-shell'
import { usePreflightCardStatuses } from './source-control-preflight-card-status'
import { translate } from '@/i18n/i18n'
import { tokenProviderStatusLabel } from './token-source-control-status'

export function BitbucketIntegrationCard(): React.JSX.Element {
const { statuses, unavailable, refresh } = usePreflightCardStatuses('bitbucket')
Expand Down Expand Up @@ -32,15 +33,7 @@ export function BitbucketIntegrationCard(): React.JSX.Element {
}
checking={status === 'checking'}
statusTone={connected ? 'connected' : 'attention'}
statusLabel={
connected
? 'Connected'
: status === 'unavailable'
? 'Unavailable'
: status === 'not-configured'
? 'Not configured'
: 'Auth failed'
}
statusLabel={tokenProviderStatusLabel({ configured: connected, status })}
>
{status !== 'checking' && !connected ? (
<IntegrationCardDetails>
Expand Down Expand Up @@ -154,17 +147,11 @@ export function AzureDevOpsIntegrationCard(): React.JSX.Element {
}
checking={status === 'checking'}
statusTone={configured ? 'connected' : 'attention'}
statusLabel={
configured
? statuses.azureDevOpsAccount
? 'Connected'
: 'Configured'
: status === 'unavailable'
? 'Unavailable'
: status === 'not-configured'
? 'Not configured'
: 'Auth failed'
}
statusLabel={tokenProviderStatusLabel({
configured,
hasAccount: Boolean(statuses.azureDevOpsAccount),
status
})}
>
{status !== 'checking' && !configured ? (
<IntegrationCardDetails>
Expand Down Expand Up @@ -283,17 +270,12 @@ export function GiteaIntegrationCard(): React.JSX.Element {
}
checking={status === 'checking'}
statusTone={configured ? 'connected' : 'attention'}
statusLabel={
configured
? statuses.giteaAccount
? 'Connected'
: 'Configured'
: status === 'unavailable'
? 'Unavailable'
: status === 'not-configured'
? 'Optional setup'
: 'Auth failed'
}
statusLabel={tokenProviderStatusLabel({
configured,
hasAccount: Boolean(statuses.giteaAccount),
status,
optional: true
})}
>
{status !== 'checking' && !configured ? (
<IntegrationCardDetails>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { translate } from '@/i18n/i18n'
import type { AzureDevOpsStatus, BitbucketStatus, GiteaStatus } from './integrations-pane-status'

/**
* Card status as the pane computes it: the provider's own state, or
* `'unavailable'` when the runtime cannot report on it at all.
*/
export type TokenProviderStatus = BitbucketStatus | AzureDevOpsStatus | GiteaStatus | 'unavailable'

const NS = 'auto.components.settings.token.source.control.integration.cards'

/**
* Status copy for the token-configured source-control cards (Bitbucket, Azure
* DevOps, Gitea). They share the same states, so the labels live in one place.
*/
export function tokenProviderStatusLabel(input: {
/** Credentials present for the provider. */
configured: boolean
/** An account was resolved with those credentials. */
hasAccount?: boolean
status: TokenProviderStatus
/** Gitea works read-only without a token, so its unconfigured state is softer. */
optional?: boolean
}): string {
if (input.configured) {
return input.hasAccount === false
? translate(`${NS}.statusConfigured`, 'Configured')
: translate(`${NS}.statusConnected`, 'Connected')
}
if (input.status === 'unavailable') {
return translate(`${NS}.statusUnavailable`, 'Unavailable')
}
if (input.status === 'not-configured') {
return input.optional
? translate(`${NS}.statusOptionalSetup`, 'Optional setup')
: translate(`${NS}.statusNotConfigured`, 'Not configured')
}
return translate(`${NS}.statusAuthFailed`, 'Auth failed')
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
44 changes: 44 additions & 0 deletions src/renderer/src/i18n/integration-card-status-localization.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* Integration cards build their status pill inside the JSX call, which the
* coverage audit does not inspect — it looks at JSX attributes and object
* properties, not conditional expressions passed to a prop. These assertions
* pin the catalog contract so a card that goes back to a bare literal fails here.
*/
import { describe, expect, it } from 'vitest'
import en from './locales/en.json'

function lookup(key: string): string | undefined {
const value = key
.split('.')
.reduce<unknown>(
(node, part) =>
node && typeof node === 'object' ? (node as Record<string, unknown>)[part] : undefined,
en as unknown
)
return typeof value === 'string' ? value : undefined
}

const REQUIRED_KEYS: Record<string, string> = {
// Linear and other task trackers
'auto.components.settings.task.tracker.integration.cards.statusConnected': 'Connected',
'auto.components.settings.task.tracker.integration.cards.statusNotConnected': 'Not connected',
// Jira
'auto.components.settings.jira.integration.card.statusConnected': 'Connected',
'auto.components.settings.jira.integration.card.statusNotConnected': 'Not connected',
// Bitbucket, Azure DevOps, Gitea
'auto.components.settings.token.source.control.integration.cards.statusConnected': 'Connected',
'auto.components.settings.token.source.control.integration.cards.statusConfigured': 'Configured',
'auto.components.settings.token.source.control.integration.cards.statusUnavailable':
'Unavailable',
'auto.components.settings.token.source.control.integration.cards.statusNotConfigured':
'Not configured',
'auto.components.settings.token.source.control.integration.cards.statusOptionalSetup':
'Optional setup',
'auto.components.settings.token.source.control.integration.cards.statusAuthFailed': 'Auth failed'
}

describe('integration card status labels', () => {
it.each(Object.entries(REQUIRED_KEYS))('keeps %s in the English catalog', (key, expected) => {
expect(lookup(key)).toBe(expected)
})
})
16 changes: 13 additions & 3 deletions src/renderer/src/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -8450,7 +8450,9 @@
"d5c5b47bb9": "update",
"fb854902d9": "connecting",
"9a9f8d4910": "Connect Jira Cloud to browse, create, and link issues.",
"74f3063026": "{{value0}} site{{value1}} connected"
"74f3063026": "{{value0}} site{{value1}} connected",
"statusConnected": "Connected",
"statusNotConnected": "Not connected"
}
}
},
Expand Down Expand Up @@ -9273,7 +9275,9 @@
"disconnect_all": "Disconnect",
"account_scope_prefix": "Account scope",
"2d60ec7921": "Connect a Jira Cloud site with an API token, or a self-hosted Jira with a personal access token or username and password. Credentials are sent to the selected remote runtime and stored there with runtime-supported encryption.",
"977e360b71": "Connect a Jira Cloud site with an API token, or a self-hosted Jira with a personal access token or username and password. Credentials are stored locally and encrypted when local runtime storage supports it."
"977e360b71": "Connect a Jira Cloud site with an API token, or a self-hosted Jira with a personal access token or username and password. Credentials are stored locally and encrypted when local runtime storage supports it.",
"statusConnected": "Connected",
"statusNotConnected": "Not connected"
}
}
}
Expand Down Expand Up @@ -9314,7 +9318,13 @@
"63a7f47392": "ORCA_BITBUCKET_EMAIL",
"24ac1c69dc": "Bitbucket status is not available in this runtime yet.",
"a924e8dcd1": "Pull requests and build statuses via Bitbucket Cloud API tokens.",
"0fa5629dad": "Pull requests and build statuses"
"0fa5629dad": "Pull requests and build statuses",
"statusConnected": "Connected",
"statusUnavailable": "Unavailable",
"statusNotConfigured": "Not configured",
"statusAuthFailed": "Auth failed",
"statusConfigured": "Configured",
"statusOptionalSetup": "Optional setup"
}
}
}
Expand Down