mirror of
https://github.com/stablyai/orca.git
synced 2026-09-22 00:02:31 +00:00
Polish setup guide integration layout (#5856)
This commit is contained in:
@@ -1007,9 +1007,6 @@
|
||||
"auto.components.feature.wall.ConnectIntegrationsList.33b650af52": {
|
||||
"ko": "팀이 작업을 추적하는 서비스를 연결하세요. Orca는 이슈 제목, 링크, 컨텍스트가 이미 첨부된 워크스페이스를 시작합니다."
|
||||
},
|
||||
"auto.components.feature.wall.ConnectIntegrationsList.3a1fcdddad": {
|
||||
"ko": "두 단계만 거치면 됩니다. 먼저 코드 리뷰 서비스를 연결한 다음, 팀이 작업을 계획하는 서비스를 연결하세요."
|
||||
},
|
||||
"auto.components.feature.wall.ConnectIntegrationsList.3dddb2d565": {
|
||||
"ko": "작업용으로 연결됨"
|
||||
},
|
||||
|
||||
@@ -10,8 +10,12 @@ import {
|
||||
JiraIntegrationCard,
|
||||
LinearIntegrationCard
|
||||
} from '@/components/settings/task-tracker-integration-cards'
|
||||
import {
|
||||
IntegrationCardGroup,
|
||||
IntegrationCardPresentationProvider
|
||||
} from '@/components/settings/integration-card-presentation'
|
||||
import { useIntegrationProviderStatusRefresh } from '@/components/settings/use-integration-provider-status-refresh'
|
||||
import { IntegrationProgress, IntegrationStep } from './connect-integration-step'
|
||||
import { IntegrationStep } from './connect-integration-step'
|
||||
import {
|
||||
deriveIntegrationFlowState,
|
||||
useIntegrationConnectionStatus
|
||||
@@ -91,100 +95,98 @@ export function ConnectIntegrationsList(): React.JSX.Element {
|
||||
const taskExpanded = taskToggleCurrent ? taskToggle.expanded : reviewDone && !trackerDone
|
||||
|
||||
return (
|
||||
<div className="space-y-2.5">
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<p className="text-[13px] leading-snug text-muted-foreground">
|
||||
{translate(
|
||||
'auto.components.feature.wall.ConnectIntegrationsList.3a1fcdddad',
|
||||
'Two quick steps: connect where your code is reviewed, then where your team plans work.'
|
||||
<IntegrationCardPresentationProvider value="setup-guide">
|
||||
<div className="space-y-2.5">
|
||||
<IntegrationStep
|
||||
index={0}
|
||||
state={flow.review}
|
||||
expanded={reviewExpanded}
|
||||
title={translate(
|
||||
'auto.components.feature.wall.ConnectIntegrationsList.review_step_title',
|
||||
'See PR status while agents work'
|
||||
)}
|
||||
</p>
|
||||
<IntegrationProgress states={[flow.review, flow.task]} />
|
||||
</div>
|
||||
description={translate(
|
||||
'auto.components.feature.wall.ConnectIntegrationsList.review_step_description',
|
||||
'Connect a review provider so Orca can show PR or MR status, checks, and reviews.'
|
||||
)}
|
||||
summary={
|
||||
<>
|
||||
<span className="font-semibold text-foreground">{status.reviewProviderName}</span>{' '}
|
||||
{translate(
|
||||
'auto.components.feature.wall.ConnectIntegrationsList.5b3577a492',
|
||||
'connected for review status'
|
||||
)}
|
||||
</>
|
||||
}
|
||||
onToggle={() => setReviewReopened((value) => !value)}
|
||||
canToggle={reviewCanToggle}
|
||||
>
|
||||
<IntegrationCardGroup>
|
||||
<GitHubIntegrationCard />
|
||||
<GitLabIntegrationCard />
|
||||
<BitbucketIntegrationCard />
|
||||
<AzureDevOpsIntegrationCard />
|
||||
<GiteaIntegrationCard />
|
||||
</IntegrationCardGroup>
|
||||
</IntegrationStep>
|
||||
|
||||
<IntegrationStep
|
||||
index={0}
|
||||
state={flow.review}
|
||||
expanded={reviewExpanded}
|
||||
title={translate(
|
||||
'auto.components.feature.wall.ConnectIntegrationsList.review_step_title',
|
||||
'See PR status while agents work'
|
||||
)}
|
||||
description={translate(
|
||||
'auto.components.feature.wall.ConnectIntegrationsList.review_step_description',
|
||||
'Connect a review provider so Orca can show PR or MR status, checks, and reviews.'
|
||||
)}
|
||||
summary={
|
||||
<>
|
||||
<span className="font-semibold text-foreground">{status.reviewProviderName}</span>{' '}
|
||||
<IntegrationStep
|
||||
index={1}
|
||||
state={flow.task}
|
||||
expanded={taskExpanded}
|
||||
title={translate(
|
||||
'auto.components.feature.wall.ConnectIntegrationsList.task_step_title',
|
||||
'Start agents on your tasks without leaving Orca'
|
||||
)}
|
||||
description={translate(
|
||||
'auto.components.feature.wall.ConnectIntegrationsList.33b650af52',
|
||||
'Connect where your team tracks work. Orca starts workspaces with the issue title, link, and context already attached.'
|
||||
)}
|
||||
summary={
|
||||
status.trackerProviderName ? (
|
||||
<>
|
||||
<TaskSourceNameList names={status.taskSourceNames} />{' '}
|
||||
{translate(
|
||||
'auto.components.feature.wall.ConnectIntegrationsList.3dddb2d565',
|
||||
'connected for tasks'
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<span className="font-semibold text-foreground">
|
||||
{status.codeHostTaskProviderName}
|
||||
</span>{' '}
|
||||
{translate(
|
||||
'auto.components.feature.wall.ConnectIntegrationsList.code_host_tasks_summary',
|
||||
'issues available as tasks · add Linear or Jira if your team plans work there'
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
onToggle={() =>
|
||||
setTaskToggle({
|
||||
expanded: !taskExpanded,
|
||||
whenTrackerDone: trackerDone,
|
||||
whenReviewDone: reviewDone
|
||||
})
|
||||
}
|
||||
>
|
||||
<IntegrationCardGroup>
|
||||
<LinearIntegrationCard />
|
||||
<JiraIntegrationCard />
|
||||
</IntegrationCardGroup>
|
||||
<p className="px-1 pt-0.5 text-[12px] leading-snug text-muted-foreground">
|
||||
{translate(
|
||||
'auto.components.feature.wall.ConnectIntegrationsList.5b3577a492',
|
||||
'connected for review status'
|
||||
'auto.components.feature.wall.ConnectIntegrationsList.code_host_tasks_caption',
|
||||
"Your code host's issues also work as tasks."
|
||||
)}
|
||||
</>
|
||||
}
|
||||
onToggle={() => setReviewReopened((value) => !value)}
|
||||
canToggle={reviewCanToggle}
|
||||
>
|
||||
<GitHubIntegrationCard />
|
||||
<GitLabIntegrationCard />
|
||||
<BitbucketIntegrationCard />
|
||||
<AzureDevOpsIntegrationCard />
|
||||
<GiteaIntegrationCard />
|
||||
</IntegrationStep>
|
||||
|
||||
<IntegrationStep
|
||||
index={1}
|
||||
state={flow.task}
|
||||
expanded={taskExpanded}
|
||||
title={translate(
|
||||
'auto.components.feature.wall.ConnectIntegrationsList.task_step_title',
|
||||
'Start agents on your tasks without leaving Orca'
|
||||
)}
|
||||
description={translate(
|
||||
'auto.components.feature.wall.ConnectIntegrationsList.33b650af52',
|
||||
'Connect where your team tracks work. Orca starts workspaces with the issue title, link, and context already attached.'
|
||||
)}
|
||||
summary={
|
||||
status.trackerProviderName ? (
|
||||
<>
|
||||
<TaskSourceNameList names={status.taskSourceNames} />{' '}
|
||||
{translate(
|
||||
'auto.components.feature.wall.ConnectIntegrationsList.3dddb2d565',
|
||||
'connected for tasks'
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<span className="font-semibold text-foreground">
|
||||
{status.codeHostTaskProviderName}
|
||||
</span>{' '}
|
||||
{translate(
|
||||
'auto.components.feature.wall.ConnectIntegrationsList.code_host_tasks_summary',
|
||||
'issues available as tasks · add Linear or Jira if your team plans work there'
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
onToggle={() =>
|
||||
setTaskToggle({
|
||||
expanded: !taskExpanded,
|
||||
whenTrackerDone: trackerDone,
|
||||
whenReviewDone: reviewDone
|
||||
})
|
||||
}
|
||||
>
|
||||
<LinearIntegrationCard />
|
||||
<JiraIntegrationCard />
|
||||
<p className="px-1 pt-1 text-[12px] leading-snug text-muted-foreground">
|
||||
{translate(
|
||||
'auto.components.feature.wall.ConnectIntegrationsList.code_host_tasks_caption',
|
||||
"Your code host's issues also work as tasks."
|
||||
)}
|
||||
</p>
|
||||
<GitHubIntegrationCard />
|
||||
<GitLabIntegrationCard />
|
||||
</IntegrationStep>
|
||||
</div>
|
||||
</p>
|
||||
<IntegrationCardGroup>
|
||||
<GitHubIntegrationCard />
|
||||
<GitLabIntegrationCard />
|
||||
</IntegrationCardGroup>
|
||||
</IntegrationStep>
|
||||
</div>
|
||||
</IntegrationCardPresentationProvider>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -279,19 +279,21 @@ export function FeatureWallSetupChecklist(
|
||||
? 'gap-8 xl:grid-cols-[minmax(0,1fr)_auto] xl:gap-12'
|
||||
: 'gap-8 sm:grid-cols-[minmax(0,48ch)_auto] sm:gap-10'
|
||||
|
||||
// Why: in the modal, a stacked checklist can leave medium-width windows with
|
||||
// only a tiny action pane; switch to the rail/content layout sooner.
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'grid h-full min-h-0',
|
||||
isEmbedded
|
||||
? 'grid-rows-[auto_minmax(0,1fr)] gap-10 lg:grid-cols-[minmax(15rem,17rem)_minmax(0,1fr)] lg:grid-rows-[minmax(0,1fr)] lg:gap-16'
|
||||
: 'grid-rows-[auto_minmax(0,1fr)] gap-5 lg:grid-cols-[minmax(200px,300px)_minmax(0,1fr)] lg:grid-rows-[minmax(0,1fr)]'
|
||||
: 'grid-rows-[auto_minmax(0,1fr)] gap-5 md:grid-cols-[minmax(190px,260px)_minmax(0,1fr)] md:grid-rows-[minmax(0,1fr)]'
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
'scrollbar-sleek min-h-0 space-y-5 overflow-y-auto',
|
||||
isEmbedded ? 'pr-4' : 'pr-1'
|
||||
isEmbedded ? 'pr-4' : 'max-h-[min(18rem,40vh)] pr-1 md:max-h-none'
|
||||
)}
|
||||
>
|
||||
<SetupSection
|
||||
@@ -325,7 +327,7 @@ export function FeatureWallSetupChecklist(
|
||||
'scrollbar-sleek min-h-0 overflow-y-auto',
|
||||
isEmbedded
|
||||
? 'pt-10 lg:border-l lg:border-border/50 lg:pl-14 lg:pt-0'
|
||||
: 'border-t border-border pt-5 lg:border-l lg:border-t-0 lg:pl-7 lg:pt-0'
|
||||
: 'border-t border-border pt-5 md:border-l md:border-t-0 md:pl-7 md:pt-0'
|
||||
)}
|
||||
>
|
||||
{activeStep ? (
|
||||
|
||||
@@ -92,28 +92,8 @@ export function IntegrationStep(props: {
|
||||
) : null}
|
||||
</button>
|
||||
{expanded ? (
|
||||
<div className="space-y-2 border-t border-border bg-muted/30 p-3">{props.children}</div>
|
||||
<div className="space-y-2 border-t border-border bg-card p-3">{props.children}</div>
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Two progress dots tracking step state; the active one stretches into a bar.
|
||||
export function IntegrationProgress(props: {
|
||||
states: readonly IntegrationStepState[]
|
||||
}): React.JSX.Element {
|
||||
return (
|
||||
<div className="flex items-center gap-1.5 pt-2" aria-hidden>
|
||||
{props.states.map((state, i) => (
|
||||
<span
|
||||
key={i}
|
||||
className={cn(
|
||||
'h-[7px] rounded-full transition-all',
|
||||
state === 'active' ? 'w-[22px] bg-foreground' : 'w-[7px]',
|
||||
state === 'done' ? 'bg-status-success' : state !== 'active' && 'bg-border'
|
||||
)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2,15 +2,15 @@ import { ExternalLink, Github, Gitlab, Terminal } from 'lucide-react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { useAppStore } from '@/store'
|
||||
import { IntegrationCardDetails, IntegrationCardShell } from './integration-card-shell'
|
||||
import {
|
||||
useIntegrationCommandRowClass,
|
||||
useIntegrationSubordinateRowClass
|
||||
} from './integration-card-presentation'
|
||||
import { getProviderAccountScope } from './provider-account-scope'
|
||||
import { ProviderHostScopeControl } from './ProviderHostScopeControl'
|
||||
import { usePreflightCardStatuses } from './source-control-preflight-card-status'
|
||||
import { translate } from '@/i18n/i18n'
|
||||
|
||||
const INTEGRATION_COMMAND_ROW_CLASS =
|
||||
'flex items-center gap-2 rounded-md border border-border/50 bg-muted/50 px-3 py-2 font-mono text-xs'
|
||||
const INTEGRATION_SUBORDINATE_ROW_CLASS = 'rounded-md border border-border/50 bg-muted/50 px-3 py-2'
|
||||
|
||||
function ProviderAccountScopeDetails({
|
||||
children
|
||||
}: {
|
||||
@@ -18,6 +18,7 @@ function ProviderAccountScopeDetails({
|
||||
}): React.JSX.Element {
|
||||
const settings = useAppStore((s) => s.settings)
|
||||
const accountScope = getProviderAccountScope(settings)
|
||||
const subordinateRowClass = useIntegrationSubordinateRowClass('text-xs')
|
||||
|
||||
return (
|
||||
<IntegrationCardDetails>
|
||||
@@ -27,7 +28,7 @@ function ProviderAccountScopeDetails({
|
||||
'Account scope'
|
||||
)}
|
||||
scope={accountScope}
|
||||
className={`text-xs ${INTEGRATION_SUBORDINATE_ROW_CLASS}`}
|
||||
className={subordinateRowClass}
|
||||
/>
|
||||
{children}
|
||||
</IntegrationCardDetails>
|
||||
@@ -38,6 +39,7 @@ export function GitHubIntegrationCard(): React.JSX.Element {
|
||||
const { statuses, unavailable, refresh } = usePreflightCardStatuses('gh')
|
||||
const status = unavailable ? 'unavailable' : statuses.ghStatus
|
||||
const connected = status === 'connected'
|
||||
const commandRowClass = useIntegrationCommandRowClass()
|
||||
|
||||
return (
|
||||
<IntegrationCardShell
|
||||
@@ -126,7 +128,7 @@ export function GitHubIntegrationCard(): React.JSX.Element {
|
||||
'The GitHub CLI is installed but not authenticated. Run this command in a terminal:'
|
||||
)}
|
||||
</p>
|
||||
<div className={INTEGRATION_COMMAND_ROW_CLASS}>
|
||||
<div className={commandRowClass}>
|
||||
<Terminal className="size-3.5 shrink-0 text-muted-foreground" />
|
||||
{translate(
|
||||
'auto.components.settings.cli.source.control.integration.cards.8d90249d22',
|
||||
@@ -166,6 +168,7 @@ export function GitLabIntegrationCard(): React.JSX.Element {
|
||||
const { statuses, unavailable, refresh } = usePreflightCardStatuses('glab')
|
||||
const status = unavailable ? 'unavailable' : statuses.glabStatus
|
||||
const connected = status === 'connected'
|
||||
const commandRowClass = useIntegrationCommandRowClass()
|
||||
|
||||
return (
|
||||
<IntegrationCardShell
|
||||
@@ -256,7 +259,7 @@ export function GitLabIntegrationCard(): React.JSX.Element {
|
||||
'The GitLab CLI is installed but not authenticated. Run this command in a terminal:'
|
||||
)}
|
||||
</p>
|
||||
<div className={INTEGRATION_COMMAND_ROW_CLASS}>
|
||||
<div className={commandRowClass}>
|
||||
<Terminal className="size-3.5 shrink-0 text-muted-foreground" />
|
||||
{translate(
|
||||
'auto.components.settings.cli.source.control.integration.cards.707180d09c',
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
import { createContext, useContext } from 'react'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
type IntegrationCardPresentation = 'default' | 'setup-guide'
|
||||
|
||||
const IntegrationCardPresentationContext = createContext<IntegrationCardPresentation>('default')
|
||||
|
||||
export function IntegrationCardPresentationProvider(props: {
|
||||
value: IntegrationCardPresentation
|
||||
children: React.ReactNode
|
||||
}): React.JSX.Element {
|
||||
return (
|
||||
<IntegrationCardPresentationContext.Provider value={props.value}>
|
||||
{props.children}
|
||||
</IntegrationCardPresentationContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export function useIntegrationCardPresentation(): IntegrationCardPresentation {
|
||||
return useContext(IntegrationCardPresentationContext)
|
||||
}
|
||||
|
||||
export function useIntegrationCardShellClass(className?: string): string {
|
||||
const presentation = useIntegrationCardPresentation()
|
||||
return cn(
|
||||
presentation === 'setup-guide'
|
||||
? 'bg-transparent px-4 py-3'
|
||||
: 'rounded-xl border border-border bg-card px-4 py-3.5 shadow-xs',
|
||||
className
|
||||
)
|
||||
}
|
||||
|
||||
export function IntegrationCardGroup(props: {
|
||||
children: React.ReactNode
|
||||
className?: string
|
||||
}): React.JSX.Element {
|
||||
const presentation = useIntegrationCardPresentation()
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
presentation === 'setup-guide'
|
||||
? 'overflow-hidden rounded-lg border border-border/50 bg-card/30 divide-y divide-border/40'
|
||||
: 'space-y-3',
|
||||
props.className
|
||||
)}
|
||||
>
|
||||
{props.children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function useIntegrationSubordinateRowClass(className?: string): string {
|
||||
const presentation = useIntegrationCardPresentation()
|
||||
return cn(
|
||||
presentation === 'setup-guide'
|
||||
? 'border-t border-border/40 px-0 py-2 first:border-t-0'
|
||||
: 'rounded-md border border-border/50 bg-muted/50 px-3 py-2',
|
||||
className
|
||||
)
|
||||
}
|
||||
|
||||
export function useIntegrationCommandRowClass(): string {
|
||||
const presentation = useIntegrationCardPresentation()
|
||||
return cn(
|
||||
'flex items-center gap-2 font-mono text-xs',
|
||||
presentation === 'setup-guide'
|
||||
? 'border-t border-border/40 px-0 py-2'
|
||||
: 'rounded-md border border-border/50 bg-muted/50 px-3 py-2'
|
||||
)
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { LoaderCircle } from 'lucide-react'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useIntegrationCardShellClass } from './integration-card-presentation'
|
||||
|
||||
export type IntegrationCardStatusTone = 'connected' | 'attention' | 'neutral'
|
||||
|
||||
@@ -20,6 +21,7 @@ export function IntegrationCardShell(props: {
|
||||
actions?: React.ReactNode
|
||||
children?: React.ReactNode
|
||||
}): React.JSX.Element {
|
||||
const shellClass = useIntegrationCardShellClass(props.className)
|
||||
const status = props.checking ? (
|
||||
<LoaderCircle className="size-4 shrink-0 animate-spin text-muted-foreground" />
|
||||
) : (
|
||||
@@ -34,12 +36,7 @@ export function IntegrationCardShell(props: {
|
||||
)
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'rounded-xl border border-border bg-card px-4 py-3.5 shadow-xs',
|
||||
props.className
|
||||
)}
|
||||
>
|
||||
<div className={shellClass}>
|
||||
<div className="flex flex-wrap items-start gap-3">
|
||||
<span className="shrink-0 text-muted-foreground">{props.icon}</span>
|
||||
<div className="min-w-0 flex-1 basis-[16rem] space-y-0.5">
|
||||
|
||||
@@ -10,12 +10,12 @@ import {
|
||||
} from '@/lib/provider-runtime-context'
|
||||
import { useAppStore } from '@/store'
|
||||
import { IntegrationCardDetails, IntegrationCardShell } from './integration-card-shell'
|
||||
import { useIntegrationSubordinateRowClass } from './integration-card-presentation'
|
||||
import { getProviderAccountScope } from './provider-account-scope'
|
||||
import { ProviderHostScopeControl } from './ProviderHostScopeControl'
|
||||
import { translate } from '@/i18n/i18n'
|
||||
|
||||
type VerificationResult = { state: 'ok' | 'error'; error?: string }
|
||||
const INTEGRATION_SUBORDINATE_ROW_CLASS = 'rounded-md border border-border/50 bg-muted/50 px-3 py-2'
|
||||
|
||||
export function JiraIntegrationCard(): React.JSX.Element {
|
||||
const jiraStatus = useAppStore((s) => s.jiraStatus)
|
||||
@@ -40,6 +40,8 @@ export function JiraIntegrationCard(): React.JSX.Element {
|
||||
const credentialCopy = hasRemoteProviderRuntime(settings)
|
||||
? 'Connect a Jira Cloud site with your Atlassian email and an API token. Credentials are sent to the selected remote runtime and stored there with runtime-supported encryption.'
|
||||
: 'Connect a Jira Cloud site with your Atlassian email and an API token. Credentials are stored locally and encrypted when local runtime storage supports it.'
|
||||
const subordinateRowClass = useIntegrationSubordinateRowClass('flex items-center gap-3')
|
||||
const accountScopeRowClass = useIntegrationSubordinateRowClass('text-xs')
|
||||
|
||||
const handleDisconnect = async (siteId?: string): Promise<void> => {
|
||||
await disconnectJira(siteId)
|
||||
@@ -117,7 +119,7 @@ export function JiraIntegrationCard(): React.JSX.Element {
|
||||
'Account scope'
|
||||
)}
|
||||
scope={accountScope}
|
||||
className={`text-xs ${INTEGRATION_SUBORDINATE_ROW_CLASS}`}
|
||||
className={accountScopeRowClass}
|
||||
/>
|
||||
{connected && sites.length > 0 ? (
|
||||
<div className="space-y-2">
|
||||
@@ -125,10 +127,7 @@ export function JiraIntegrationCard(): React.JSX.Element {
|
||||
const testResult = testResultBySite[site.id]
|
||||
const testing = testingSiteId === site.id
|
||||
return (
|
||||
<div
|
||||
key={site.id}
|
||||
className={`flex items-center gap-3 ${INTEGRATION_SUBORDINATE_ROW_CLASS}`}
|
||||
>
|
||||
<div key={site.id} className={subordinateRowClass}>
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="truncate text-sm font-medium text-foreground">
|
||||
{site.displayName}
|
||||
|
||||
@@ -7,12 +7,12 @@ import { useMountedRef } from '@/hooks/useMountedRef'
|
||||
import { getProviderRuntimeContextKey } from '@/lib/provider-runtime-context'
|
||||
import { useAppStore } from '@/store'
|
||||
import { IntegrationCardDetails, IntegrationCardShell } from './integration-card-shell'
|
||||
import { useIntegrationSubordinateRowClass } from './integration-card-presentation'
|
||||
import { getProviderAccountScope } from './provider-account-scope'
|
||||
import { ProviderHostScopeControl } from './ProviderHostScopeControl'
|
||||
import { translate } from '@/i18n/i18n'
|
||||
|
||||
type VerificationResult = { state: 'ok' | 'error'; error?: string }
|
||||
const INTEGRATION_SUBORDINATE_ROW_CLASS = 'rounded-md border border-border/50 bg-muted/50 px-3 py-2'
|
||||
|
||||
export function LinearIntegrationCard(): React.JSX.Element {
|
||||
const linearStatus = useAppStore((s) => s.linearStatus)
|
||||
@@ -36,6 +36,7 @@ export function LinearIntegrationCard(): React.JSX.Element {
|
||||
const connected = contextMatches && linearStatus.connected
|
||||
const workspaces = linearStatus.workspaces ?? []
|
||||
const accountScope = getProviderAccountScope(settings)
|
||||
const subordinateRowClass = useIntegrationSubordinateRowClass('flex items-center gap-3')
|
||||
|
||||
const handleDisconnect = async (workspaceId?: string): Promise<void> => {
|
||||
await (workspaceId ? disconnectLinearWorkspace(workspaceId) : disconnectLinear())
|
||||
@@ -116,10 +117,7 @@ export function LinearIntegrationCard(): React.JSX.Element {
|
||||
const testResult = testResultByWorkspace[workspace.id]
|
||||
const testing = testingWorkspaceId === workspace.id
|
||||
return (
|
||||
<div
|
||||
key={workspace.id}
|
||||
className={`flex items-center gap-3 ${INTEGRATION_SUBORDINATE_ROW_CLASS}`}
|
||||
>
|
||||
<div key={workspace.id} className={subordinateRowClass}>
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="truncate text-sm font-medium text-foreground">
|
||||
{workspace.organizationName}
|
||||
@@ -217,6 +215,8 @@ export function LinearIntegrationCard(): React.JSX.Element {
|
||||
}
|
||||
|
||||
function ProviderAccountScopeRow({ scope }: { scope: ReturnType<typeof getProviderAccountScope> }) {
|
||||
const subordinateRowClass = useIntegrationSubordinateRowClass('text-xs')
|
||||
|
||||
return (
|
||||
<ProviderHostScopeControl
|
||||
labelPrefix={translate(
|
||||
@@ -224,7 +224,7 @@ function ProviderAccountScopeRow({ scope }: { scope: ReturnType<typeof getProvid
|
||||
'Account scope'
|
||||
)}
|
||||
scope={scope}
|
||||
className={`text-xs ${INTEGRATION_SUBORDINATE_ROW_CLASS}`}
|
||||
className={subordinateRowClass}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -10146,7 +10146,6 @@
|
||||
"3dddb2d565": "connected for tasks",
|
||||
"33b650af52": "Connect where your team tracks work. Orca starts workspaces with the issue title, link, and context already attached.",
|
||||
"5b3577a492": "connected for review status",
|
||||
"3a1fcdddad": "Two quick steps: connect where your code is reviewed, then where your team plans work.",
|
||||
"list_end": ", and ",
|
||||
"list_pair": " and ",
|
||||
"list_mid": ", ",
|
||||
|
||||
@@ -10146,7 +10146,6 @@
|
||||
"3dddb2d565": "connected for tasks",
|
||||
"33b650af52": "Connect where your team tracks work. Orca starts workspaces with the issue title, link, and context already attached.",
|
||||
"5b3577a492": "connected for review status",
|
||||
"3a1fcdddad": "Two quick steps: connect where your code is reviewed, then where your team plans work.",
|
||||
"list_end": ", and ",
|
||||
"list_pair": " and ",
|
||||
"list_mid": ", ",
|
||||
|
||||
@@ -10146,7 +10146,6 @@
|
||||
"3dddb2d565": "connected for tasks",
|
||||
"33b650af52": "Connect where your team tracks work. Orca starts workspaces with the issue title, link, and context already attached.",
|
||||
"5b3577a492": "connected for review status",
|
||||
"3a1fcdddad": "Two quick steps: connect where your code is reviewed, then where your team plans work.",
|
||||
"list_end": ", and ",
|
||||
"list_pair": " and ",
|
||||
"list_mid": ", ",
|
||||
|
||||
@@ -10146,7 +10146,6 @@
|
||||
"3dddb2d565": "작업용으로 연결됨",
|
||||
"33b650af52": "팀이 작업을 추적하는 서비스를 연결하세요. Orca는 이슈 제목, 링크, 컨텍스트가 이미 첨부된 워크스페이스를 시작합니다.",
|
||||
"5b3577a492": "리뷰 상태용으로 연결됨",
|
||||
"3a1fcdddad": "두 단계만 거치면 됩니다. 먼저 코드 리뷰 서비스를 연결한 다음, 팀이 작업을 계획하는 서비스를 연결하세요.",
|
||||
"list_end": ", 그리고 ",
|
||||
"list_pair": " and ",
|
||||
"list_mid": ", ",
|
||||
|
||||
@@ -10146,7 +10146,6 @@
|
||||
"3dddb2d565": "connected for tasks",
|
||||
"33b650af52": "Connect where your team tracks work. Orca starts workspaces with the issue title, link, and context already attached.",
|
||||
"5b3577a492": "connected for review status",
|
||||
"3a1fcdddad": "Two quick steps: connect where your code is reviewed, then where your team plans work.",
|
||||
"list_end": ", and ",
|
||||
"list_pair": " and ",
|
||||
"list_mid": ", ",
|
||||
|
||||
Reference in New Issue
Block a user