feature: Add Jira, Jira DC and Linear UI Integrations (#9761)

Co-authored-by: Wishmi Dhanapala <wishmis@verdentra.com>
This commit is contained in:
Bashwara Undupitiya
2025-08-01 20:55:49 +05:30
committed by GitHub
parent 4f24bcaec9
commit d0a8c896c2
41 changed files with 1950 additions and 22 deletions

View File

@@ -37,7 +37,16 @@
"usage/cloud/bitbucket-installation",
"usage/cloud/github-installation",
"usage/cloud/gitlab-installation",
"usage/cloud/slack-installation"
"usage/cloud/slack-installation",
{
"group": "Project Management Tools",
"pages": [
"usage/cloud/project-management/overview",
"usage/cloud/project-management/jira-integration",
"usage/cloud/project-management/jira-dc-integration",
"usage/cloud/project-management/linear-integration"
]
}
]
},
"usage/cloud/cloud-ui",

BIN
docs/static/img/workspace-admin-edit.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

BIN
docs/static/img/workspace-configure.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

BIN
docs/static/img/workspace-link.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
docs/static/img/workspace-user-edit.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -0,0 +1,118 @@
---
title: Jira Data Center Integration (Beta)
description: Complete guide for setting up Jira Data Center integration with OpenHands Cloud, including service account creation, personal access token generation, webhook configuration, and workspace integration setup.
---
# Jira Data Center Integration
## Platform Configuration
### Step 1: Create Service Account
1. **Access User Management**
- Log in to Jira Data Center as administrator
- Go to **Administration** > **User Management**
2. **Create User**
- Click **Create User**
- Username: `openhands-agent`
- Full Name: `OpenHands Agent`
- Email: `openhands@yourcompany.com` (replace with your preferred service account email)
- Password: Set a secure password
- Click **Create**
3. **Assign Permissions**
- Add user to appropriate groups
- Ensure access to relevant projects
- Grant necessary project permissions
### Step 2: Generate API Token
1. **Personal Access Tokens**
- Log in as the service account
- Go to **Profile** > **Personal Access Tokens**
- Click **Create token**
- Name: `OpenHands Cloud Integration`
- Expiry: Set appropriate expiration (recommend 1 year)
- Click **Create**
- **Important**: Copy and store the token securely
### Step 3: Configure Webhook
1. **Create Webhook**
- Go to **Administration** > **System** > **WebHooks**
- Click **Create a WebHook**
- **Name**: `OpenHands Cloud Integration`
- **URL**: `https://app.all-hands.dev/integration/jira-dc/events`
- Set a suitable webhook secret
- **Issue related events**: Select the following:
- Issue updated
- Comment created
- **JQL Filter**: Leave empty (or customize as needed)
- Click **Create**
- **Important**: Copy and store the webhook secret securely (you'll need this for workspace integration)
---
## Workspace Integration
### Step 1: Log in to OpenHands Cloud
1. **Navigate and Authenticate**
- Go to [OpenHands Cloud](https://app.all-hands.dev/)
- Sign in with your Git provider (GitHub, GitLab, or BitBucket)
- **Important:** Make sure you're signing in with the same Git provider account that contains the repositories you want the OpenHands agent to work on.
### Step 2: Configure Jira Data Center Integration
1. **Access Integration Settings**
- Navigate to **Settings** > **Integrations**
- Locate **Jira Data Center** section
2. **Configure Workspace**
- Click **Configure** button
- Enter your workspace name and click **Connect**
- If no integration exists, you'll be prompted to enter additional credentials required for the workspace integration:
- **Webhook Secret**: The webhook secret from Step 3 above
- **Service Account Email**: The service account email from Step 1 above
- **Service Account API Key**: The personal access token from Step 2 above
- Ensure **Active** toggle is enabled
3. **Complete OAuth Flow**
- You'll be redirected to Jira Data Center to complete OAuth verification
- Grant the necessary permissions to verify your workspace access. If you have access to multiple workspaces, select the correct one that you initially provided
- If successful, you will be redirected back to the **Integrations** settings in the OpenHands Cloud UI
### Managing Your Integration
**Edit Configuration:**
- Click the **Edit** button next to your configured platform
- Update any necessary credentials or settings
- Click **Update** to apply changes
- You will need to repeat the OAuth flow as before
- **Important:** Only the original user who created the integration can see the edit view
**Unlink Workspace:**
- In the edit view, click **Unlink** next to the workspace name
- This will deactivate your workspace link
- **Important:** If the original user who configured the integration chooses to unlink their integration, any users currently linked to that integration will also be unlinked, and the workspace integration will be deactivated. The integration can only be reactivated by the original user.
### Screenshots
<AccordionGroup>
<Accordion title="Workspace link flow">
![workspace-link.png](/static/img/workspace-link.png)
</Accordion>
<Accordion title="Workspace Configure flow">
![workspace-link.png](/static/img/workspace-configure.png)
</Accordion>
<Accordion title="Edit view as a user">
![workspace-link.png](/static/img/workspace-user-edit.png)
</Accordion>
<Accordion title="Edit view as the workspace creator">
![workspace-link.png](/static/img/workspace-admin-edit.png)
</Accordion>
</AccordionGroup>

View File

@@ -0,0 +1,123 @@
---
title: Jira Cloud Integration
description: Complete guide for setting up Jira Cloud integration with OpenHands Cloud, including service account creation, API token generation, webhook configuration, and workspace integration setup.
---
# Jira Cloud Integration
## Platform Configuration
### Step 1: Create Service Account
1. **Navigate to User Management**
- Go to [Atlassian Admin](https://admin.atlassian.com/)
- Select your organization
- Go to **Directory** > **Users**
2. **Create OpenHands Service Account**
- Click **Add user**
- Email: `openhands@yourcompany.com` (replace with your preferred service account email)
- Display name: `OpenHands Agent`
- Send invitation: **No** (you'll set password manually)
- Click **Add user**
3. **Configure Account**
- Locate the created user and click on it
- Set a secure password
- Add to relevant Jira projects with appropriate permissions
### Step 2: Generate API Token
1. **Access API Token Management**
- Log in as the OpenHands service account
- Go to [API Tokens](https://id.atlassian.com/manage-profile/security/api-tokens)
2. **Create API Token**
- Click **Create API token**
- Label: `OpenHands Cloud Integration`
- Expiry: Set appropriate expiration (recommend 1 year)
- Click **Create**
- **Important**: Copy and securely store the token immediately
### Step 3: Configure Webhook
1. **Navigate to Webhook Settings**
- Go to **Jira Settings** > **System** > **WebHooks**
- Click **Create a WebHook**
2. **Configure Webhook**
- **Name**: `OpenHands Cloud Integration`
- **Status**: Enabled
- **URL**: `https://app.all-hands.dev/integration/jira/events`
- **Issue related events**: Select the following:
- Issue updated
- Comment created
- **JQL Filter**: Leave empty (or customize as needed)
- Click **Create**
- **Important**: Copy and store the webhook secret securely (you'll need this for workspace integration)
---
## Workspace Integration
### Step 1: Log in to OpenHands Cloud
1. **Navigate and Authenticate**
- Go to [OpenHands Cloud](https://app.all-hands.dev/)
- Sign in with your Git provider (GitHub, GitLab, or BitBucket)
- **Important:** Make sure you're signing in with the same Git provider account that contains the repositories you want the OpenHands agent to work on.
### Step 2: Configure Jira Integration
1. **Access Integration Settings**
- Navigate to **Settings** > **Integrations**
- Locate **Jira Cloud** section
2. **Configure Workspace**
- Click **Configure** button
- Enter your workspace name and click **Connect**
- **Important:** Make sure you enter the full workspace name, eg: **yourcompany.atlassian.net**
- If no integration exists, you'll be prompted to enter additional credentials required for the workspace integration:
- **Webhook Secret**: The webhook secret from Step 3 above
- **Service Account Email**: The service account email from Step 1 above
- **Service Account API Key**: The API token from Step 2 above
- Ensure **Active** toggle is enabled
3. **Complete OAuth Flow**
- You'll be redirected to Jira Cloud to complete OAuth verification
- Grant the necessary permissions to verify your workspace access.
- If successful, you will be redirected back to the **Integrations** settings in the OpenHands Cloud UI
### Managing Your Integration
**Edit Configuration:**
- Click the **Edit** button next to your configured platform
- Update any necessary credentials or settings
- Click **Update** to apply changes
- You will need to repeat the OAuth flow as before
- **Important:** Only the original user who created the integration can see the edit view
**Unlink Workspace:**
- In the edit view, click **Unlink** next to the workspace name
- This will deactivate your workspace link
- **Important:** If the original user who configured the integration chooses to unlink their integration, any users currently linked to that workspace integration will also be unlinked, and the workspace integration will be deactivated. The integration can only be reactivated by the original user.
### Screenshots
<AccordionGroup>
<Accordion title="Workspace link flow">
![workspace-link.png](/static/img/workspace-link.png)
</Accordion>
<Accordion title="Workspace Configure flow">
![workspace-link.png](/static/img/workspace-configure.png)
</Accordion>
<Accordion title="Edit view as a user">
![workspace-link.png](/static/img/workspace-user-edit.png)
</Accordion>
<Accordion title="Edit view as the workspace creator">
![workspace-link.png](/static/img/workspace-admin-edit.png)
</Accordion>
</AccordionGroup>

View File

@@ -0,0 +1,122 @@
---
title: Linear Integration
description: Complete guide for setting up Linear integration with OpenHands Cloud, including service account creation, API key generation, webhook configuration, and workspace integration setup.
---
# Linear Integration
## Platform Configuration
### Step 1: Create Service Account
1. **Access Team Settings**
- Log in to Linear as a team admin
- Go to **Settings** > **Members**
2. **Invite Service Account**
- Click **Invite members**
- Email: `openhands@yourcompany.com` (replace with your preferred service account email)
- Role: **Member** (with appropriate team access)
- Send invitation
3. **Complete Setup**
- Accept invitation from the service account email
- Complete profile setup
- Ensure access to relevant teams/workspaces
### Step 2: Generate API Key
1. **Access API Settings**
- Log in as the service account
- Go to **Settings** > **API**
2. **Create Personal API Key**
- Click **Create new key**
- Name: `OpenHands Cloud Integration`
- Scopes: Select the following:
- `Read` - Read access to issues and comments
- `Create comments` - Ability to create or update comments
- Select the teams you want to provide access to, or allow access for all teams you have permissions for
- Click **Create**
- **Important**: Copy and store the API key securely
### Step 3: Configure Webhook
1. **Access Webhook Settings**
- Go to **Settings** > **API** > **Webhooks**
- Click **New webhook**
2. **Configure Webhook**
- **Label**: `OpenHands Cloud Integration`
- **URL**: `https://app.all-hands.dev/integration/linear/events`
- **Resource types**: Select:
- `Comment` - For comment events
- `Issue` - For issue updates (label changes)
- Select the teams you want to provide access to, or allow access for all public teams
- Click **Create webhook**
- **Important**: Copy and store the webhook secret securely (you'll need this for workspace integration)
---
## Workspace Integration
### Step 1: Log in to OpenHands Cloud
1. **Navigate and Authenticate**
- Go to [OpenHands Cloud](https://app.all-hands.dev/)
- Sign in with your Git provider (GitHub, GitLab, or BitBucket)
- **Important:** Make sure you're signing in with the same Git provider account that contains the repositories you want the OpenHands agent to work on.
### Step 2: Configure Linear Integration
1. **Access Integration Settings**
- Navigate to **Settings** > **Integrations**
- Locate **Linear** section
2. **Configure Workspace**
- Click **Configure** button
- Enter your workspace name and click **Connect**
- If no integration exists, you'll be prompted to enter additional credentials required for the workspace integration:
- **Webhook Secret**: The webhook secret from Step 3 above
- **Service Account Email**: The service account email from Step 1 above
- **Service Account API Key**: The API key from Step 2 above
- Ensure **Active** toggle is enabled
3. **Complete OAuth Flow**
- You'll be redirected to Linear to complete OAuth verification
- Grant the necessary permissions to verify your workspace access. If you have access to multiple workspaces, select the correct one that you initially provided
- If successful, you will be redirected back to the **Integrations** settings in the OpenHands Cloud UI
### Managing Your Integration
**Edit Configuration:**
- Click the **Edit** button next to your configured platform
- Update any necessary credentials or settings
- Click **Update** to apply changes
- You will need to repeat the OAuth flow as before
- **Important:** Only the original user who created the integration can see the edit view
**Unlink Workspace:**
- In the edit view, click **Unlink** next to the workspace name
- This will deactivate your workspace link
- **Important:** If the original user who configured the integration chooses to unlink their integration, any users currently linked to that integration will also be unlinked, and the workspace integration will be deactivated. The integration can only be reactivated by the original user.
### Screenshots
<AccordionGroup>
<Accordion title="Workspace link flow">
![workspace-link.png](/static/img/workspace-link.png)
</Accordion>
<Accordion title="Workspace Configure flow">
![workspace-link.png](/static/img/workspace-configure.png)
</Accordion>
<Accordion title="Edit view as a user">
![workspace-link.png](/static/img/workspace-user-edit.png)
</Accordion>
<Accordion title="Edit view as the workspace creator">
![workspace-link.png](/static/img/workspace-admin-edit.png)
</Accordion>
</AccordionGroup>

View File

@@ -0,0 +1,79 @@
---
title: Project Management Tool Integrations
description: Overview of OpenHands Cloud integrations with project management platforms including Jira Cloud, Jira Data Center, and Linear. Learn about setup requirements, usage methods, and troubleshooting.
---
# Project Management Tool Integrations
## Overview
OpenHands Cloud integrates with project management platforms (Jira Cloud, Jira Data Center, and Linear) to enable AI-powered task delegation. Users can invoke the OpenHands agent by:
- Adding `@openhands` in ticket comments
- Adding the `openhands` label to tickets
## Prerequisites
Integration requires two levels of setup:
1. **Platform Configuration** - Administrative setup of service accounts and webhooks on your project management platform (see individual platform documentation below)
2. **Workspace Integration** - Self-service configuration through the OpenHands Cloud UI to link your OpenHands account to the target workspace
### Platform-Specific Setup Guides:
- [Jira Cloud Integration](./jira-integration.md)
- [Jira Data Center Integration](./jira-dc-integration.md)
- [Linear Integration](./linear-integration.md)
## Usage
Once both the platform configuration and workspace integration are completed, users can trigger the OpenHands agent within their project management platforms using two methods:
### Method 1: Comment Mention
Add a comment to any issue with `@openhands` followed by your task description:
```
@openhands Please implement the user authentication feature described in this ticket
```
### Method 2: Label-based Delegation
Add the label `openhands` to any issue. The OpenHands agent will automatically process the issue based on its description and requirements.
### Git Repository Detection
The OpenHands agent needs to identify which Git repository to work with when processing your issues. Here's how to ensure proper repository detection:
#### Specifying the Target Repository
**Required:** Include the target Git repository in your issue description or comment to ensure the agent works with the correct codebase.
**Supported Repository Formats:**
- Full HTTPS URL: `https://github.com/owner/repository.git`
- GitHub URL without .git: `https://github.com/owner/repository`
- Owner/repository format: `owner/repository`
#### Platform-Specific Behavior
**Linear Integration:** When GitHub integration is enabled for your Linear workspace with issue sync activated, the target repository is automatically detected from the linked GitHub issue. Manual specification is not required in this configuration.
**Jira Integrations:** Always include the repository information in your issue description or `@openhands` comment to ensure proper repository detection.
## Troubleshooting
### Platform Configuration Issues
- **Webhook not triggering**: Verify the webhook URL is correct and the proper event types are selected (Comment, Issue updated)
- **API authentication failing**: Check API key/token validity and ensure required scopes are granted
- **Permission errors**: Ensure the service account has access to relevant projects/teams and appropriate permissions
### Workspace Integration Issues
- **Workspace linking requests credentials**: If there are no active workspace integrations for the workspace you specified, you need to configure it first. Contact your platform administrator that you want to integrate with (eg: Jira, Linear)
- **OAuth flow fails**: Ensure you're signing in with the same Git provider account that contains the repositories you want OpenHands to work on
- **Integration not found**: Verify the workspace name matches exactly and that platform configuration was completed first
### General Issues
- **Agent not responding**: Check webhook logs in your platform settings and verify service account status
- **Authentication errors**: Verify Git provider permissions and OpenHands Cloud access
- **Partial functionality**: Ensure both platform configuration and workspace integration are properly completed
### Getting Help
For additional support, contact OpenHands Cloud support with:
- Your integration platform (Linear, Jira Cloud, or Jira Data Center)
- Workspace name
- Error logs from webhook/integration attempts
- Screenshots of configuration settings (without sensitive credentials)

View File

@@ -120,6 +120,9 @@ describe("ExpandableMessage", () => {
FEATURE_FLAGS: {
ENABLE_BILLING: true,
HIDE_LLM_SETTINGS: false,
ENABLE_JIRA: false,
ENABLE_JIRA_DC: false,
ENABLE_LINEAR: false,
},
});
const RouterStub = createRoutesStub([

View File

@@ -28,6 +28,9 @@ describe("PaymentForm", () => {
FEATURE_FLAGS: {
ENABLE_BILLING: true,
HIDE_LLM_SETTINGS: false,
ENABLE_JIRA: false,
ENABLE_JIRA_DC: false,
ENABLE_LINEAR: false,
},
});
});

View File

@@ -76,6 +76,9 @@ describe("frontend/routes/_oh", () => {
FEATURE_FLAGS: {
ENABLE_BILLING: false,
HIDE_LLM_SETTINGS: false,
ENABLE_JIRA: false,
ENABLE_JIRA_DC: false,
ENABLE_LINEAR: false,
},
});
@@ -111,6 +114,9 @@ describe("frontend/routes/_oh", () => {
FEATURE_FLAGS: {
ENABLE_BILLING: false,
HIDE_LLM_SETTINGS: false,
ENABLE_JIRA: false,
ENABLE_JIRA_DC: false,
ENABLE_LINEAR: false,
},
});
@@ -192,6 +198,9 @@ describe("frontend/routes/_oh", () => {
FEATURE_FLAGS: {
ENABLE_BILLING: false,
HIDE_LLM_SETTINGS: false,
ENABLE_JIRA: false,
ENABLE_JIRA_DC: false,
ENABLE_LINEAR: false,
},
});

View File

@@ -17,6 +17,9 @@ const VALID_OSS_CONFIG: GetConfigResponse = {
FEATURE_FLAGS: {
ENABLE_BILLING: false,
HIDE_LLM_SETTINGS: false,
ENABLE_JIRA: false,
ENABLE_JIRA_DC: false,
ENABLE_LINEAR: false,
},
};
@@ -27,6 +30,9 @@ const VALID_SAAS_CONFIG: GetConfigResponse = {
FEATURE_FLAGS: {
ENABLE_BILLING: false,
HIDE_LLM_SETTINGS: false,
ENABLE_JIRA: false,
ENABLE_JIRA_DC: false,
ENABLE_LINEAR: false,
},
};

View File

@@ -333,6 +333,9 @@ describe("Settings 404", () => {
FEATURE_FLAGS: {
ENABLE_BILLING: false,
HIDE_LLM_SETTINGS: false,
ENABLE_JIRA: false,
ENABLE_JIRA_DC: false,
ENABLE_LINEAR: false,
},
});
const error = createAxiosNotFoundErrorObject();
@@ -355,6 +358,9 @@ describe("Setup Payment modal", () => {
FEATURE_FLAGS: {
ENABLE_BILLING: true,
HIDE_LLM_SETTINGS: false,
ENABLE_JIRA: false,
ENABLE_JIRA_DC: false,
ENABLE_LINEAR: false,
},
});
const error = createAxiosNotFoundErrorObject();

View File

@@ -86,6 +86,9 @@ describe("Settings Billing", () => {
FEATURE_FLAGS: {
ENABLE_BILLING: false,
HIDE_LLM_SETTINGS: false,
ENABLE_JIRA: false,
ENABLE_JIRA_DC: false,
ENABLE_LINEAR: false,
},
});
@@ -104,6 +107,9 @@ describe("Settings Billing", () => {
FEATURE_FLAGS: {
ENABLE_BILLING: true,
HIDE_LLM_SETTINGS: false,
ENABLE_JIRA: false,
ENABLE_JIRA_DC: false,
ENABLE_LINEAR: false,
},
});
@@ -122,6 +128,9 @@ describe("Settings Billing", () => {
FEATURE_FLAGS: {
ENABLE_BILLING: true,
HIDE_LLM_SETTINGS: false,
ENABLE_JIRA: false,
ENABLE_JIRA_DC: false,
ENABLE_LINEAR: false,
},
});

View File

@@ -55,6 +55,9 @@ export interface GetConfigResponse {
ENABLE_BILLING: boolean;
HIDE_LLM_SETTINGS: boolean;
HIDE_MICROAGENT_MANAGEMENT?: boolean;
ENABLE_JIRA: boolean;
ENABLE_JIRA_DC: boolean;
ENABLE_LINEAR: boolean;
};
MAINTENANCE?: {
startTime: string;

View File

@@ -12,16 +12,21 @@ export function ConfigureGitHubRepositoriesAnchor({
const { t } = useTranslation();
return (
<a
data-testid="configure-github-repositories-button"
href={`https://github.com/apps/${slug}/installations/new`}
target="_blank"
rel="noreferrer noopener"
className="py-9"
>
<BrandButton type="button" variant="secondary">
<div data-testid="configure-github-repositories-button" className="py-9">
<BrandButton
type="button"
variant="primary"
className="w-55"
onClick={() =>
window.open(
`https://github.com/apps/${slug}/installations/new`,
"_blank",
"noreferrer noopener",
)
}
>
{t(I18nKey.GITHUB$CONFIGURE_REPOS)}
</BrandButton>
</a>
</div>
);
}

View File

@@ -6,16 +6,21 @@ export function InstallSlackAppAnchor() {
const { t } = useTranslation();
return (
<a
data-testid="install-slack-app-button"
href="https://slack.com/oauth/v2/authorize?client_id=7477886716822.8729519890534&scope=app_mentions:read,channels:history,chat:write,groups:history,im:history,mpim:history,users:read&user_scope="
target="_blank"
rel="noreferrer noopener"
className="py-9"
>
<BrandButton type="button" variant="secondary">
<div data-testid="install-slack-app-button" className="py-9">
<BrandButton
type="button"
variant="primary"
className="w-55"
onClick={() =>
window.open(
"https://slack.com/oauth/v2/authorize?client_id=7477886716822.8729519890534&scope=app_mentions:read,channels:history,chat:write,groups:history,im:history,mpim:history,users:read&user_scope=",
"_blank",
"noreferrer noopener",
)
}
>
{t(I18nKey.SLACK$INSTALL_APP)}
</BrandButton>
</a>
</div>
);
}

View File

@@ -0,0 +1,440 @@
import React, { useState } from "react";
import { Trans, useTranslation } from "react-i18next";
import { I18nKey } from "#/i18n/declaration";
import { BrandButton } from "#/components/features/settings/brand-button";
import { SettingsInput } from "#/components/features/settings/settings-input";
import { ModalBackdrop } from "#/components/shared/modals/modal-backdrop";
import { ModalBody } from "#/components/shared/modals/modal-body";
import {
BaseModalDescription,
BaseModalTitle,
} from "#/components/shared/modals/confirmation-modals/base-modal";
import { SettingsSwitch } from "#/components/features/settings/settings-switch";
import { useValidateIntegration } from "#/hooks/mutation/use-validate-integration";
interface ConfigureButtonProps {
onClick: () => void;
isDisabled: boolean;
text?: string;
"data-testid"?: string;
}
export function ConfigureButton({
onClick,
isDisabled,
text,
"data-testid": dataTestId,
}: ConfigureButtonProps) {
const { t } = useTranslation();
return (
<BrandButton
data-testid={dataTestId}
variant="primary"
onClick={onClick}
isDisabled={isDisabled}
type="button"
className="w-30 min-w-20"
>
{text || t(I18nKey.PROJECT_MANAGEMENT$CONFIGURE_BUTTON_LABEL)}
</BrandButton>
);
}
interface ConfigureModalProps {
isOpen: boolean;
onClose: () => void;
onConfirm: (data: {
workspace: string;
webhookSecret: string;
serviceAccountEmail: string;
serviceAccountApiKey: string;
isActive: boolean;
}) => void;
onLink: (workspace: string) => void;
onUnlink?: () => void;
platformName: string;
platform: "jira" | "jira-dc" | "linear";
integrationData?: {
id: number;
keycloak_user_id: string;
status: string;
workspace?: {
id: number;
name: string;
status: string;
editable: boolean;
};
} | null;
}
export function ConfigureModal({
isOpen,
onClose,
onConfirm,
onLink,
onUnlink,
platformName,
platform,
integrationData,
}: ConfigureModalProps) {
const { t } = useTranslation();
const [workspace, setWorkspace] = useState("");
const [webhookSecret, setWebhookSecret] = useState("");
const [serviceAccountEmail, setServiceAccountEmail] = useState("");
const [serviceAccountApiKey, setServiceAccountApiKey] = useState("");
const [isActive, setIsActive] = useState(true);
const [showConfigurationFields, setShowConfigurationFields] = useState(false);
// Determine initial state based on integrationData
const existingWorkspace = integrationData?.workspace;
const isWorkspaceEditable = existingWorkspace?.editable ?? false;
// Set initial workspace value when modal opens
React.useEffect(() => {
if (isOpen && existingWorkspace) {
setWorkspace(existingWorkspace.name);
setShowConfigurationFields(isWorkspaceEditable);
} else if (isOpen && !existingWorkspace) {
setWorkspace("");
setShowConfigurationFields(false);
}
}, [isOpen, existingWorkspace, isWorkspaceEditable]);
// Validation states
const [workspaceError, setWorkspaceError] = useState<string | null>(null);
const [webhookSecretError, setWebhookSecretError] = useState<string | null>(
null,
);
const [emailError, setEmailError] = useState<string | null>(null);
const [apiKeyError, setApiKeyError] = useState<string | null>(null);
const validateMutation = useValidateIntegration(platform, {
onSuccess: (data) => {
if (data.data.status === "active") {
// Validation successful, proceed with linking
onLink(workspace.trim());
} else {
// Show configuration fields for further setup
setShowConfigurationFields(true);
setIsActive(true);
}
},
onError: (error) => {
if (error.response?.status === 404) {
// Integration not found, show configuration fields
setShowConfigurationFields(true);
setIsActive(true);
} else {
// Other errors - still show configuration fields as fallback
setShowConfigurationFields(true);
setIsActive(true);
}
},
});
// Validation functions
const validateWorkspace = (value: string) => {
const isValid = /^[a-zA-Z0-9\-_.]*$/.test(value);
if (!isValid && value.length > 0) {
setWorkspaceError(
t(I18nKey.PROJECT_MANAGEMENT$WORKSPACE_NAME_VALIDATION_ERROR),
);
} else {
setWorkspaceError(null);
}
return isValid;
};
const validateWebhookSecret = (value: string) => {
const hasSpaces = /\s/.test(value);
if (hasSpaces) {
setWebhookSecretError(
t(I18nKey.PROJECT_MANAGEMENT$WEBHOOK_SECRET_NAME_VALIDATION_ERROR),
);
} else {
setWebhookSecretError(null);
}
return !hasSpaces;
};
const validateEmail = (value: string) => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
const isValid = emailRegex.test(value) || value.length === 0;
if (!isValid && value.length > 0) {
setEmailError(
t(I18nKey.PROJECT_MANAGEMENT$SVC_ACC_EMAIL_VALIDATION_ERROR),
);
} else {
setEmailError(null);
}
return isValid;
};
const validateApiKey = (value: string) => {
const hasSpaces = /\s/.test(value);
if (hasSpaces) {
setApiKeyError(
t(I18nKey.PROJECT_MANAGEMENT$SVC_ACC_API_KEY_VALIDATION_ERROR),
);
} else {
setApiKeyError(null);
}
return !hasSpaces;
};
// Input handlers with validation
const handleWorkspaceChange = (value: string) => {
setWorkspace(value);
validateWorkspace(value);
};
const handleWebhookSecretChange = (value: string) => {
setWebhookSecret(value);
validateWebhookSecret(value);
};
const handleEmailChange = (value: string) => {
setServiceAccountEmail(value);
validateEmail(value);
};
const handleApiKeyChange = (value: string) => {
setServiceAccountApiKey(value);
validateApiKey(value);
};
const handleClose = () => {
setWorkspace("");
setWebhookSecret("");
setServiceAccountEmail("");
setServiceAccountApiKey("");
setIsActive(false);
setShowConfigurationFields(false);
setWorkspaceError(null);
setWebhookSecretError(null);
setEmailError(null);
setApiKeyError(null);
onClose();
};
if (!isOpen) {
return null;
}
const handleConnect = () => {
if (showConfigurationFields) {
// Full configuration flow (either new configuration or editing existing)
onConfirm({
workspace,
webhookSecret,
serviceAccountEmail,
serviceAccountApiKey,
isActive,
});
} else if (!existingWorkspace) {
// First check the workspace with validation for new integrations
validateMutation.mutate(workspace.trim());
}
// For existing workspace that's not editable, no action needed
// This case shouldn't happen as the button should be hidden
};
const isConnectDisabled = showConfigurationFields
? !workspace.trim() ||
!webhookSecret.trim() ||
!serviceAccountEmail.trim() ||
!serviceAccountApiKey.trim() ||
workspaceError !== null ||
webhookSecretError !== null ||
emailError !== null ||
apiKeyError !== null ||
validateMutation.isPending
: !workspace.trim() ||
workspaceError !== null ||
validateMutation.isPending;
return (
<ModalBackdrop onClose={handleClose}>
<ModalBody className="items-start border border-tertiary w-96">
<BaseModalTitle
title={
showConfigurationFields
? t(I18nKey.PROJECT_MANAGEMENT$CONFIGURE_MODAL_TITLE, {
platform: platformName,
})
: t(I18nKey.PROJECT_MANAGEMENT$LINK_CONFIRMATION_TITLE)
}
/>
<BaseModalDescription>
{showConfigurationFields ? (
<Trans
i18nKey={I18nKey.PROJECT_MANAGEMENT$CONFIGURE_MODAL_DESCRIPTION}
components={{
a: (
<a
href="https://docs.all-hands.dev/usage/cloud/openhands-cloud"
target="_blank"
rel="noopener noreferrer"
className="text-blue-500 hover:underline"
>
Check the document for more information
</a>
),
b: <b />,
}}
/>
) : (
<p className="mt-4">
<Trans
i18nKey={
I18nKey.PROJECT_MANAGEMENT$IMPORTANT_WORKSPACE_INTEGRATION
}
components={{
b: <b />,
a: (
<a
href="https://docs.all-hands.dev/usage/cloud/openhands-cloud"
target="_blank"
rel="noopener noreferrer"
className="text-blue-500 underline"
>
Check the document for more information
</a>
),
}}
/>
</p>
)}
</BaseModalDescription>
<div className="w-full flex flex-col gap-4 mt-4">
<div>
<div className="flex gap-2 items-end">
<div className="flex-1">
<SettingsInput
label={t(I18nKey.PROJECT_MANAGEMENT$WORKSPACE_NAME_LABEL)}
placeholder={t(
I18nKey.PROJECT_MANAGEMENT$WORKSPACE_NAME_PLACEHOLDER,
)}
value={workspace}
onChange={handleWorkspaceChange}
className="w-full"
type="text"
pattern="^[a-zA-Z0-9\-_.]*$"
isDisabled={!!existingWorkspace}
/>
</div>
{existingWorkspace && onUnlink && (
<BrandButton
variant="secondary"
onClick={onUnlink}
data-testid="unlink-button"
type="button"
className="mb-0"
>
{t(I18nKey.PROJECT_MANAGEMENT$UNLINK_BUTTON_LABEL)}
</BrandButton>
)}
</div>
{workspaceError && (
<p className="text-red-500 text-sm mt-2">{workspaceError}</p>
)}
</div>
{showConfigurationFields && (
<>
<div>
<SettingsInput
label={t(I18nKey.PROJECT_MANAGEMENT$WEBHOOK_SECRET_LABEL)}
placeholder={t(
I18nKey.PROJECT_MANAGEMENT$WEBHOOK_SECRET_PLACEHOLDER,
)}
value={webhookSecret}
onChange={handleWebhookSecretChange}
className="w-full"
type="password"
/>
{webhookSecretError && (
<p className="text-red-500 text-sm mt-2">
{webhookSecretError}
</p>
)}
</div>
<div>
<SettingsInput
label={t(
I18nKey.PROJECT_MANAGEMENT$SERVICE_ACCOUNT_EMAIL_LABEL,
)}
placeholder={t(
I18nKey.PROJECT_MANAGEMENT$SERVICE_ACCOUNT_EMAIL_PLACEHOLDER,
)}
value={serviceAccountEmail}
onChange={handleEmailChange}
className="w-full"
type="email"
/>
{emailError && (
<p className="text-red-500 text-sm mt-2">{emailError}</p>
)}
</div>
<div>
<SettingsInput
label={t(
I18nKey.PROJECT_MANAGEMENT$SERVICE_ACCOUNT_API_LABEL,
)}
placeholder={t(
I18nKey.PROJECT_MANAGEMENT$SERVICE_ACCOUNT_API_PLACEHOLDER,
)}
value={serviceAccountApiKey}
onChange={handleApiKeyChange}
className="w-full"
type="password"
/>
{apiKeyError && (
<p className="text-red-500 text-sm mt-2">{apiKeyError}</p>
)}
</div>
<div className="mt-4">
<SettingsSwitch
testId="active-toggle"
onToggle={setIsActive}
isToggled={isActive}
>
{t(I18nKey.PROJECT_MANAGEMENT$ACTIVE_TOGGLE_LABEL)}
</SettingsSwitch>
</div>
</>
)}
</div>
<div className="flex flex-col gap-2 w-full mt-4">
{/* Hide the connect/edit button if workspace exists but is not editable */}
{(!existingWorkspace || isWorkspaceEditable) && (
<BrandButton
variant="primary"
onClick={handleConnect}
data-testid="connect-button"
type="button"
className="w-full"
isDisabled={isConnectDisabled}
>
{(() => {
if (existingWorkspace && showConfigurationFields) {
return t(I18nKey.PROJECT_MANAGEMENT$EDIT_BUTTON_LABEL);
}
return t(I18nKey.PROJECT_MANAGEMENT$CONNECT_BUTTON_LABEL);
})()}
</BrandButton>
)}
<BrandButton
variant="secondary"
onClick={handleClose}
data-testid="cancel-button"
type="button"
className="w-full"
>
{t(I18nKey.FEEDBACK$CANCEL_LABEL)}
</BrandButton>
</div>
</ModalBody>
</ModalBackdrop>
);
}

View File

@@ -0,0 +1,37 @@
import React from "react";
import { useTranslation } from "react-i18next";
import { BrandButton } from "#/components/features/settings/brand-button";
import { I18nKey } from "#/i18n/declaration";
interface IntegrationButtonProps {
isLoading: boolean;
isLinked: boolean;
onClick: () => void;
"data-testid"?: string;
}
export function IntegrationButton({
isLoading,
isLinked,
onClick,
"data-testid": dataTestId,
}: IntegrationButtonProps) {
const { t } = useTranslation();
return (
<BrandButton
data-testid={dataTestId}
variant={isLinked ? "secondary" : "primary"}
onClick={onClick}
isDisabled={isLoading}
type="button"
className="w-30 min-w-20"
>
{isLoading && t(I18nKey.SETTINGS$SAVING)}
{!isLoading &&
(isLinked
? t(I18nKey.PROJECT_MANAGEMENT$UNLINK_BUTTON_LABEL)
: t(I18nKey.PROJECT_MANAGEMENT$LINK_BUTTON_LABEL))}
</BrandButton>
);
}

View File

@@ -0,0 +1,110 @@
import React from "react";
import { useTranslation } from "react-i18next";
import { useIntegrationStatus } from "#/hooks/query/use-integration-status";
import { useLinkIntegration } from "#/hooks/mutation/use-link-integration";
import { useUnlinkIntegration } from "#/hooks/mutation/use-unlink-integration";
import { useConfigureIntegration } from "#/hooks/mutation/use-configure-integration";
import { I18nKey } from "#/i18n/declaration";
import {
ConfigureButton,
ConfigureModal,
} from "#/components/features/settings/project-management/configure-modal";
interface IntegrationRowProps {
platform: "jira" | "jira-dc" | "linear";
platformName: string;
"data-testid"?: string;
}
export function IntegrationRow({
platform,
platformName,
"data-testid": dataTestId,
}: IntegrationRowProps) {
const [isConfigureModalOpen, setConfigureModalOpen] = React.useState(false);
const { t } = useTranslation();
const { data: integrationData, isLoading: isStatusLoading } =
useIntegrationStatus(platform);
const linkMutation = useLinkIntegration(platform, {
onSettled: () => {
setConfigureModalOpen(false);
},
});
const unlinkMutation = useUnlinkIntegration(platform, {
onSettled: () => {
setConfigureModalOpen(false);
},
});
const configureMutation = useConfigureIntegration(platform, {
onSettled: () => {
setConfigureModalOpen(false);
},
});
const handleConfigure = () => {
setConfigureModalOpen(true);
};
const handleLink = (workspace: string) => {
linkMutation.mutate(workspace);
};
const handleUnlink = () => {
unlinkMutation.mutate();
};
const handleConfigureConfirm = (data: {
workspace: string;
webhookSecret: string;
serviceAccountEmail: string;
serviceAccountApiKey: string;
isActive: boolean;
}) => {
configureMutation.mutate(data);
};
const isLoading =
isStatusLoading ||
linkMutation.isPending ||
unlinkMutation.isPending ||
configureMutation.isPending;
// Determine if integration is active and workspace exists
const isIntegrationActive = integrationData?.status === "active";
const hasWorkspace = integrationData?.workspace;
// Determine button text based on integration state
const buttonText =
isIntegrationActive && hasWorkspace
? t(I18nKey.PROJECT_MANAGEMENT$EDIT_BUTTON_LABEL)
: t(I18nKey.PROJECT_MANAGEMENT$CONFIGURE_BUTTON_LABEL);
return (
<div className="flex items-center justify-between" data-testid={dataTestId}>
<span className="font-medium">{platformName}</span>
<div className="flex items-center gap-6">
<ConfigureButton
onClick={handleConfigure}
isDisabled={isLoading}
text={buttonText}
data-testid={`${platform}-configure-button`}
/>
</div>
<ConfigureModal
isOpen={isConfigureModalOpen}
onClose={() => setConfigureModalOpen(false)}
onConfirm={handleConfigureConfirm}
onLink={handleLink}
onUnlink={handleUnlink}
platformName={platformName}
platform={platform}
integrationData={integrationData}
/>
</div>
);
}

View File

@@ -0,0 +1,41 @@
import React from "react";
import { useTranslation } from "react-i18next";
import { I18nKey } from "#/i18n/declaration";
import { IntegrationRow } from "./integration-row";
import { useConfig } from "#/hooks/query/use-config";
export function ProjectManagementIntegration() {
const { t } = useTranslation();
const { data: config } = useConfig();
return (
<div className="flex flex-col gap-4 w-1/4">
<h3 className="text-xl font-medium text-white">
{t(I18nKey.PROJECT_MANAGEMENT$TITLE)}
</h3>
<div className="flex flex-col gap-4">
{config?.FEATURE_FLAGS?.ENABLE_JIRA && (
<IntegrationRow
platform="jira"
platformName="Jira Cloud"
data-testid="jira-integration-row"
/>
)}
{config?.FEATURE_FLAGS?.ENABLE_JIRA_DC && (
<IntegrationRow
platform="jira-dc"
platformName="Jira Data Center"
data-testid="jira-dc-integration-row"
/>
)}
{config?.FEATURE_FLAGS?.ENABLE_LINEAR && (
<IntegrationRow
platform="linear"
platformName="Linear"
data-testid="linear-integration-row"
/>
)}
</div>
</div>
);
}

View File

@@ -0,0 +1,72 @@
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { useTranslation } from "react-i18next";
import { openHands } from "#/api/open-hands-axios";
import { I18nKey } from "#/i18n/declaration";
import { displayErrorToast } from "#/utils/custom-toast-handlers";
import { retrieveAxiosErrorMessage } from "#/utils/retrieve-axios-error-message";
interface ConfigureIntegrationData {
workspace: string;
webhookSecret: string;
serviceAccountEmail: string;
serviceAccountApiKey: string;
isActive: boolean;
}
export function useConfigureIntegration(
platform: "jira" | "jira-dc" | "linear",
{
onSettled,
}: {
onSettled: () => void;
},
) {
const queryClient = useQueryClient();
const { t } = useTranslation();
return useMutation({
mutationFn: async (data: ConfigureIntegrationData) => {
const input = {
workspace_name: data.workspace,
webhook_secret: data.webhookSecret,
svc_acc_email: data.serviceAccountEmail,
svc_acc_api_key: data.serviceAccountApiKey,
is_active: data.isActive,
};
const response = await openHands.post(
`/integration/${platform}/workspaces`,
input,
);
const { success, redirect, authorizationUrl } = response.data;
if (success) {
if (redirect) {
if (authorizationUrl) {
window.location.href = authorizationUrl;
} else {
throw new Error("Could not get authorization URL from the server.");
}
} else {
window.location.reload();
}
} else {
throw new Error("Configuration failed");
}
return response.data;
},
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: ["integration-status", platform],
});
},
onError: (error) => {
const errorMessage = retrieveAxiosErrorMessage(error);
displayErrorToast(errorMessage || t(I18nKey.ERROR$GENERIC));
},
onSettled,
});
}

View File

@@ -0,0 +1,60 @@
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { useTranslation } from "react-i18next";
import { openHands } from "#/api/open-hands-axios";
import { I18nKey } from "#/i18n/declaration";
import { displayErrorToast } from "#/utils/custom-toast-handlers";
import { retrieveAxiosErrorMessage } from "#/utils/retrieve-axios-error-message";
export function useLinkIntegration(
platform: "jira" | "jira-dc" | "linear",
{
onSettled,
}: {
onSettled: () => void;
},
) {
const queryClient = useQueryClient();
const { t } = useTranslation();
return useMutation({
mutationFn: async (workspace: string) => {
const input = {
workspace_name: workspace,
};
const response = await openHands.post(
`/integration/${platform}/workspaces/link`,
input,
);
const { success, redirect, authorizationUrl } = response.data;
if (success) {
if (redirect) {
if (authorizationUrl) {
window.location.href = authorizationUrl;
} else {
throw new Error("Could not get authorization URL from the server.");
}
} else {
window.location.reload();
}
} else {
throw new Error("Link integration failed");
}
return response.data;
},
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: ["integration-status", platform],
});
},
onError: (error) => {
const errorMessage = retrieveAxiosErrorMessage(error);
displayErrorToast(errorMessage || t(I18nKey.ERROR$GENERIC));
},
onSettled,
});
}

View File

@@ -0,0 +1,38 @@
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { useTranslation } from "react-i18next";
import { openHands } from "#/api/open-hands-axios";
import { I18nKey } from "#/i18n/declaration";
import {
displayErrorToast,
displaySuccessToast,
} from "#/utils/custom-toast-handlers";
import { retrieveAxiosErrorMessage } from "#/utils/retrieve-axios-error-message";
export function useUnlinkIntegration(
platform: "jira" | "jira-dc" | "linear",
{
onSettled,
}: {
onSettled: () => void;
},
) {
const queryClient = useQueryClient();
const { t } = useTranslation();
return useMutation({
mutationFn: () =>
openHands.post(`/integration/${platform}/workspaces/unlink`),
onSuccess: () => {
displaySuccessToast(t(I18nKey.SETTINGS$SAVED));
queryClient.invalidateQueries({
queryKey: ["integration-status", platform],
});
},
onError: (error) => {
const errorMessage = retrieveAxiosErrorMessage(error);
displayErrorToast(errorMessage || t(I18nKey.ERROR$GENERIC));
},
onSettled,
});
}

View File

@@ -0,0 +1,43 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { useMutation } from "@tanstack/react-query";
import axios from "axios";
import { useTranslation } from "react-i18next";
import { openHands } from "#/api/open-hands-axios";
import { I18nKey } from "#/i18n/declaration";
import { displayErrorToast } from "#/utils/custom-toast-handlers";
import { retrieveAxiosErrorMessage } from "#/utils/retrieve-axios-error-message";
export function useValidateIntegration(
platform: "jira" | "jira-dc" | "linear",
{
onSuccess,
onError,
}: {
onSuccess: (data: any) => void;
onError: (error: any) => void;
},
) {
const { t } = useTranslation();
return useMutation({
mutationFn: (workspace?: string) => {
const workspaceParam = workspace ? `/${workspace}` : "";
return openHands.get(
`/integration/${platform}/workspaces/validate${workspaceParam}`,
);
},
onSuccess,
onError: (error) => {
if (axios.isAxiosError(error) && error.response?.status === 404) {
onError(error);
} else {
const errorMessage = retrieveAxiosErrorMessage(error);
displayErrorToast(
errorMessage ||
t(I18nKey.PROJECT_MANAGEMENT$VALIDATE_INTEGRATION_ERROR),
);
}
},
});
}

View File

@@ -0,0 +1,22 @@
import { useQuery } from "@tanstack/react-query";
import axios from "axios";
import { openHands } from "#/api/open-hands-axios";
export function useIntegrationStatus(platform: "jira" | "jira-dc" | "linear") {
return useQuery({
queryKey: ["integration-status", platform],
queryFn: async () => {
try {
const response = await openHands.get(
`/integration/${platform}/workspaces/link`,
);
return response.data;
} catch (error) {
if (axios.isAxiosError(error) && error.response?.status === 404) {
return null;
}
throw error;
}
},
});
}

View File

@@ -107,6 +107,8 @@ export enum I18nKey {
SETTINGS$NAV_CREDITS = "SETTINGS$NAV_CREDITS",
SETTINGS$NAV_SECRETS = "SETTINGS$NAV_SECRETS",
SETTINGS$NAV_API_KEYS = "SETTINGS$NAV_API_KEYS",
SETTINGS$GITHUB = "SETTINGS$GITHUB",
SETTINGS$SLACK = "SETTINGS$SLACK",
SETTINGS$NAV_LLM = "SETTINGS$NAV_LLM",
GIT$MERGE_REQUEST = "GIT$MERGE_REQUEST",
GIT$GITLAB_API = "GIT$GITLAB_API",
@@ -734,5 +736,29 @@ export enum I18nKey {
MICROAGENT_MANAGEMENT$WHAT_YOU_WOULD_LIKE_TO_KNOW_ABOUT_THIS_REPO = "MICROAGENT_MANAGEMENT$WHAT_YOU_WOULD_LIKE_TO_KNOW_ABOUT_THIS_REPO",
MICROAGENT_MANAGEMENT$DESCRIBE_WHAT_TO_KNOW_ABOUT_THIS_REPO = "MICROAGENT_MANAGEMENT$DESCRIBE_WHAT_TO_KNOW_ABOUT_THIS_REPO",
MICROAGENT_MANAGEMENT$UPDATE_MICROAGENT_MODAL_DESCRIPTION = "MICROAGENT_MANAGEMENT$UPDATE_MICROAGENT_MODAL_DESCRIPTION",
PROJECT_MANAGEMENT$TITLE = "PROJECT_MANAGEMENT$TITLE",
PROJECT_MANAGEMENT$VALIDATE_INTEGRATION_ERROR = "PROJECT_MANAGEMENT$VALIDATE_INTEGRATION_ERROR",
PROJECT_MANAGEMENT$LINK_BUTTON_LABEL = "PROJECT_MANAGEMENT$LINK_BUTTON_LABEL",
PROJECT_MANAGEMENT$UNLINK_BUTTON_LABEL = "PROJECT_MANAGEMENT$UNLINK_BUTTON_LABEL",
PROJECT_MANAGEMENT$LINK_CONFIRMATION_TITLE = "PROJECT_MANAGEMENT$LINK_CONFIRMATION_TITLE",
PROJECT_MANAGEMENT$CONFIGURE_BUTTON_LABEL = "PROJECT_MANAGEMENT$CONFIGURE_BUTTON_LABEL",
PROJECT_MANAGEMENT$EDIT_BUTTON_LABEL = "PROJECT_MANAGEMENT$EDIT_BUTTON_LABEL",
PROJECT_MANAGEMENT$WORKSPACE_NAME_LABEL = "PROJECT_MANAGEMENT$WORKSPACE_NAME_LABEL",
PROJECT_MANAGEMENT$WORKSPACE_NAME_PLACEHOLDER = "PROJECT_MANAGEMENT$WORKSPACE_NAME_PLACEHOLDER",
PROJECT_MANAGEMENT$WEBHOOK_SECRET_LABEL = "PROJECT_MANAGEMENT$WEBHOOK_SECRET_LABEL",
PROJECT_MANAGEMENT$WEBHOOK_SECRET_PLACEHOLDER = "PROJECT_MANAGEMENT$WEBHOOK_SECRET_PLACEHOLDER",
PROJECT_MANAGEMENT$SERVICE_ACCOUNT_EMAIL_LABEL = "PROJECT_MANAGEMENT$SERVICE_ACCOUNT_EMAIL_LABEL",
PROJECT_MANAGEMENT$SERVICE_ACCOUNT_EMAIL_PLACEHOLDER = "PROJECT_MANAGEMENT$SERVICE_ACCOUNT_EMAIL_PLACEHOLDER",
PROJECT_MANAGEMENT$SERVICE_ACCOUNT_API_LABEL = "PROJECT_MANAGEMENT$SERVICE_ACCOUNT_API_LABEL",
PROJECT_MANAGEMENT$SERVICE_ACCOUNT_API_PLACEHOLDER = "PROJECT_MANAGEMENT$SERVICE_ACCOUNT_API_PLACEHOLDER",
PROJECT_MANAGEMENT$CONNECT_BUTTON_LABEL = "PROJECT_MANAGEMENT$CONNECT_BUTTON_LABEL",
PROJECT_MANAGEMENT$ACTIVE_TOGGLE_LABEL = "PROJECT_MANAGEMENT$ACTIVE_TOGGLE_LABEL",
PROJECT_MANAGEMENT$CONFIGURE_MODAL_DESCRIPTION = "PROJECT_MANAGEMENT$CONFIGURE_MODAL_DESCRIPTION",
PROJECT_MANAGEMENT$CONFIGURE_MODAL_TITLE = "PROJECT_MANAGEMENT$CONFIGURE_MODAL_TITLE",
PROJECT_MANAGEMENT$IMPORTANT_WORKSPACE_INTEGRATION = "PROJECT_MANAGEMENT$IMPORTANT_WORKSPACE_INTEGRATION",
PROJECT_MANAGEMENT$WORKSPACE_NAME_VALIDATION_ERROR = "PROJECT_MANAGEMENT$WORKSPACE_NAME_VALIDATION_ERROR",
PROJECT_MANAGEMENT$WEBHOOK_SECRET_NAME_VALIDATION_ERROR = "PROJECT_MANAGEMENT$WEBHOOK_SECRET_NAME_VALIDATION_ERROR",
PROJECT_MANAGEMENT$SVC_ACC_EMAIL_VALIDATION_ERROR = "PROJECT_MANAGEMENT$SVC_ACC_EMAIL_VALIDATION_ERROR",
PROJECT_MANAGEMENT$SVC_ACC_API_KEY_VALIDATION_ERROR = "PROJECT_MANAGEMENT$SVC_ACC_API_KEY_VALIDATION_ERROR",
MICROAGENT_MANAGEMENT$ERROR_LOADING_MICROAGENT_CONTENT = "MICROAGENT_MANAGEMENT$ERROR_LOADING_MICROAGENT_CONTENT",
}

View File

@@ -1711,6 +1711,38 @@
"de": "API-Schlüssel",
"uk": "API ключі"
},
"SETTINGS$GITHUB": {
"en": "GitHub",
"ja": "GitHub",
"zh-CN": "GitHub",
"zh-TW": "GitHub",
"ko-KR": "GitHub",
"no": "GitHub",
"it": "GitHub",
"pt": "GitHub",
"es": "GitHub",
"ar": "GitHub",
"fr": "GitHub",
"tr": "GitHub",
"de": "GitHub",
"uk": "GitHub"
},
"SETTINGS$SLACK": {
"en": "Slack",
"ja": "Slack",
"zh-CN": "Slack",
"zh-TW": "Slack",
"ko-KR": "Slack",
"no": "Slack",
"it": "Slack",
"pt": "Slack",
"es": "Slack",
"ar": "Slack",
"fr": "Slack",
"tr": "Slack",
"de": "Slack",
"uk": "Slack"
},
"SETTINGS$NAV_LLM": {
"en": "LLM",
"ja": "LLM",
@@ -11743,6 +11775,390 @@
"de": "OpenHands aktualisiert den Microagenten basierend auf Ihren Anweisungen.",
"uk": "OpenHands оновить мікроагента відповідно до ваших інструкцій."
},
"PROJECT_MANAGEMENT$TITLE": {
"en": "Project Management",
"ja": "プロジェクト管理",
"zh-CN": "项目管理",
"zh-TW": "專案管理",
"ko-KR": "프로젝트 관리",
"no": "Prosjektledelse",
"it": "Gestione del progetto",
"pt": "Gerenciamento de projetos",
"es": "Gestión de proyectos",
"ar": "إدارة المشاريع",
"fr": "Gestion de projet",
"tr": "Proje Yönetimi",
"de": "Projektmanagement",
"uk": "Управління проектами"
},
"PROJECT_MANAGEMENT$VALIDATE_INTEGRATION_ERROR": {
"en": "Failed to validate integration. Please try again later.",
"ja": "統合の検証に失敗しました。しばらくしてからもう一度お試しください.",
"zh-CN": "无法验证集成。请稍后重试.",
"zh-TW": "無法驗證整合。請稍後重試.",
"ko-KR": "통합을 검증하지 못했습니다. 나중에 다시 시도해 주세요.",
"no": "Kunne ikke validere integrasjonen. Prøv på nytt senere.",
"it": "Impossibile convalidare l'integrazione. Riprova più tardi.",
"pt": "Falha ao validar a integração. Tente novamente mais tarde.",
"es": "No se pudo validar la integración. Inténtelo de nuevo más tarde.",
"ar": "فشل التحقق من التكامل. يُرجى المحاولة لاحقًا.",
"fr": "Échec de la validation de l'intégration. Veuillez réessayer ultérieurement.",
"tr": "Entegrasyon doğrulanamadı. Lütfen daha sonra tekrar deneyin.",
"de": "Die Integration konnte nicht validiert werden. Bitte versuchen Sie es später noch einmal.",
"uk": "Не вдалося перевірити інтеграцію. Спробуйте пізніше."
},
"PROJECT_MANAGEMENT$LINK_BUTTON_LABEL": {
"en": "Link",
"ja": "リンク",
"zh-CN": "关联",
"zh-TW": "關聯",
"ko-KR": "링크",
"no": "Link",
"it": "Collegamento",
"pt": "Link",
"es": "Enlace",
"ar": "وصلة",
"fr": "Lien",
"tr": "Bağlantı",
"de": "Link",
"uk": "Посилання"
},
"PROJECT_MANAGEMENT$UNLINK_BUTTON_LABEL": {
"en": "Unlink",
"ja": "リンクを解除する",
"zh-CN": "取消链接",
"zh-TW": "取消連結",
"ko-KR": "풀리다",
"no": "Fjern tilknytningen",
"it": "Scollega",
"pt": "Desvincular",
"es": "Desconectar",
"ar": "إلغاء الارتباط",
"fr": "Dissocier",
"tr": "Bağlantıyı kaldır",
"de": "Verknüpfung aufheben",
"uk": "Від’єднати"
},
"PROJECT_MANAGEMENT$LINK_CONFIRMATION_TITLE": {
"en": "Link Workspace",
"ja": "ワークスペースをリンク",
"zh-CN": "链接工作区",
"zh-TW": "連結工作區",
"ko-KR": "작업공간 링크",
"no": "Link arbeidsområde",
"it": "Collega area di lavoro",
"pt": "Vincular espaço de trabalho",
"es": "Vincular espacio de trabajo",
"ar": "ربط مساحة العمل",
"fr": "Lier l'espace de travail",
"tr": "Çalışma Alanını Bağla",
"de": "Arbeitsbereich verknüpfen",
"uk": "Пов'язати робочу область"
},
"PROJECT_MANAGEMENT$CONFIGURE_BUTTON_LABEL": {
"en": "Configure",
"ja": "設定する",
"zh-CN": "配置",
"zh-TW": "配置",
"ko-KR": "구성",
"no": "Konfigurer",
"it": "Configura",
"pt": "Configurar",
"es": "Configurar",
"ar": "تكوين",
"fr": "Configurer",
"tr": "Yapılandır",
"de": "Konfigurieren",
"uk": "Налаштувати"
},
"PROJECT_MANAGEMENT$EDIT_BUTTON_LABEL": {
"en": "Edit",
"ja": "編集",
"zh-CN": "编辑",
"zh-TW": "編輯",
"ko-KR": "편집",
"no": "Rediger",
"it": "Modifica",
"pt": "Editar",
"es": "Editar",
"ar": "تحرير",
"fr": "Modifier",
"tr": "Düzenle",
"de": "Bearbeiten",
"uk": "Редагувати"
},
"PROJECT_MANAGEMENT$WORKSPACE_NAME_LABEL": {
"en": "Workspace Name",
"ja": "ワークスペース名",
"zh-CN": "工作区名称",
"zh-TW": "工作區名稱",
"ko-KR": "작업공간 이름",
"no": "Navn på arbeidsområde",
"it": "Nome dell'area di lavoro",
"pt": "Nome do espaço de trabalho",
"es": "Nombre del espacio de trabajo",
"ar": "اسم مساحة العمل",
"fr": "Nom de l'espace de travail",
"tr": "Çalışma Alanı Adı",
"de": "Arbeitsbereichsname",
"uk": "Назва робочої області"
},
"PROJECT_MANAGEMENT$WORKSPACE_NAME_PLACEHOLDER": {
"en": "myworkspace",
"ja": "私のワークスペース",
"zh-CN": "我的工作空间",
"zh-TW": "我的工作區",
"ko-KR": "내워크스페이스",
"no": "mittarbeidsområde",
"it": "mioworkspace",
"pt": "meuworkspace",
"es": "miespaciodetrabajo",
"ar": "مساحةعملي",
"fr": "monworkspace",
"tr": "benimworkspace",
"de": "meinarbeitsbereich",
"uk": "моя-робоча-область"
},
"PROJECT_MANAGEMENT$WEBHOOK_SECRET_LABEL": {
"en": "Webhook Secret",
"ja": "Webhook シークレット",
"zh-CN": "Webhook 秘密",
"zh-TW": "Webhook 秘密",
"ko-KR": "웹훅 비밀",
"no": "Webhook Secret",
"it": "Segreto del webhook",
"pt": "Segredo do webhook",
"es": "Secreto del webhook",
"ar": "سر الويب هوك",
"fr": "Secret du webhook",
"tr": "Web Kancası Sırrı",
"de": "Webhook-Geheimnis",
"uk": "Секрет вебхуку"
},
"PROJECT_MANAGEMENT$WEBHOOK_SECRET_PLACEHOLDER": {
"en": "whsec_abcd1234efgh5678ijkl",
"ja": "whsec_abcd1234efgh5678ijkl",
"zh-CN": "whsec_abcd1234efgh5678ijkl",
"zh-TW": "whsec_abcd1234efgh5678ijkl",
"ko-KR": "whsec_abcd1234efgh5678ijkl",
"no": "whsec_abcd1234efgh5678ijkl",
"it": "whsec_abcd1234efgh5678ijkl",
"pt": "whsec_abcd1234efgh5678ijkl",
"es": "whsec_abcd1234efgh5678ijkl",
"ar": "whsec_abcd1234efgh5678ijkl",
"fr": "whsec_abcd1234efgh5678ijkl",
"tr": "whsec_abcd1234efgh5678ijkl",
"de": "whsec_abcd1234efgh5678ijkl",
"uk": "whsec_abcd1234efgh5678ijkl"
},
"PROJECT_MANAGEMENT$SERVICE_ACCOUNT_EMAIL_LABEL": {
"en": "Service Account Email",
"ja": "サービスアカウントのメールアドレス",
"zh-CN": "服务帐户电子邮件",
"zh-TW": "服務帳號電子郵件",
"ko-KR": "서비스 계정 이메일",
"no": "Tjenestekonto e-post",
"it": "E-mail dell'account di servizio",
"pt": "E-mail da conta de serviço",
"es": "Correo electrónico de cuenta de servicio",
"ar": "البريد الإلكتروني لحساب الخدمة",
"fr": "E-mail du compte de service",
"tr": "Hizmet Hesabı E-postası",
"de": "E-Mail-Adresse des Dienstkontos",
"uk": "Електронна адреса облікового запису служби"
},
"PROJECT_MANAGEMENT$SERVICE_ACCOUNT_EMAIL_PLACEHOLDER": {
"en": "test@example.com",
"ja": "test@example.com",
"zh-CN": "test@example.com",
"zh-TW": "test@example.com",
"ko-KR": "test@example.com",
"no": "test@example.com",
"it": "test@example.com",
"pt": "test@example.com",
"es": "test@example.com",
"ar": "test@example.com",
"fr": "test@example.com",
"tr": "test@example.com",
"de": "test@example.com",
"uk": "test@example.com"
},
"PROJECT_MANAGEMENT$SERVICE_ACCOUNT_API_LABEL": {
"en": "Service Account API Key",
"ja": "サービスアカウントAPIキー",
"zh-CN": "服务帐户 API 密钥",
"zh-TW": "服務帳戶 API 金鑰",
"ko-KR": "서비스 계정 API 키",
"no": "Tjenestekonto API-nøkkel",
"it": "Chiave API dell'account di servizio",
"pt": "Chave de API da conta de serviço",
"es": "Clave API de cuenta de servicio",
"ar": "مفتاح API لحساب الخدمة",
"fr": "Clé API du compte de service",
"tr": "Hizmet Hesabı API Anahtarı",
"de": "API-Schlüssel des Dienstkontos",
"uk": "Ключ API облікового запису служби"
},
"PROJECT_MANAGEMENT$SERVICE_ACCOUNT_API_PLACEHOLDER": {
"en": "sk-1234567890abcdef...",
"ja": "sk-1234567890abcdef...",
"zh-CN": "sk-1234567890abcdef...",
"zh-TW": "sk-1234567890abcdef...",
"ko-KR": "sk-1234567890abcdef...",
"no": "sk-1234567890abcdef...",
"it": "sk-1234567890abcdef...",
"pt": "sk-1234567890abcdef...",
"es": "sk-1234567890abcdef...",
"ar": "sk-1234567890abcdef...",
"fr": "sk-1234567890abcdef...",
"tr": "sk-1234567890abcdef...",
"de": "sk-1234567890abcdef...",
"uk": "sk-1234567890abcdef..."
},
"PROJECT_MANAGEMENT$CONNECT_BUTTON_LABEL": {
"en": "Connect",
"ja": "接続する",
"zh-CN": "连接",
"zh-TW": "連接",
"ko-KR": "연결하다",
"no": "Koble til",
"it": "Collegare",
"pt": "Conectar",
"es": "Conectar",
"ar": "يتصل",
"fr": "Connecter",
"tr": "Bağlamak",
"de": "Verbinden",
"uk": "Підключитися"
},
"PROJECT_MANAGEMENT$ACTIVE_TOGGLE_LABEL": {
"en": "Active",
"ja": "アクティブ",
"zh-CN": "积极的",
"zh-TW": "積極的",
"ko-KR": "활동적인",
"no": "Aktiv",
"it": "Attiva",
"pt": "Ativa",
"es": "Activa",
"ar": "نشيط",
"fr": "Actif",
"tr": "Aktif",
"de": "Aktiv",
"uk": "Активний"
},
"PROJECT_MANAGEMENT$CONFIGURE_MODAL_DESCRIPTION": {
"en": "<b>Important:</b> Check the <a>documentation</a> for more information about configuring the workspace integration or updating an existing integration.",
"ja": "<b>重要:</b> ワークスペース統合の設定や既存の統合の更新について詳しくは<a>ドキュメント</a>をご確認ください。",
"zh-CN": "<b>重要提示:</b>有关配置工作区集成或更新现有集成的更多信息,请查看<a>文档</a>。",
"zh-TW": "<b>重要提示:</b>有關配置工作區整合或更新現有整合的更多資訊,請查看<a>文件</a>。",
"ko-KR": "<b>중요:</b> 작업공간 통합 구성이나 기존 통합 업데이트에 대한 자세한 내용은 <a>설명서</a>를 확인하세요.",
"no": "<b>Viktig:</b> Se <a>dokumentasjonen</a> for mer informasjon om konfigurering av arbeidsområdeintegrasjon eller oppdatering av eksisterende integrasjon.",
"it": "<b>Importante:</b> Consulta la <a>documentazione</a> per ulteriori informazioni sulla configurazione dell'integrazione dell'area di lavoro o sull'aggiornamento di un'integrazione esistente.",
"pt": "<b>Importante:</b> Consulte a <a>documentação</a> para obter mais informações sobre como configurar a integração do workspace ou atualizar uma integração existente.",
"es": "<b>Importante:</b> Consulte la <a>documentación</a> para obtener más información sobre la configuración de la integración del espacio de trabajo o la actualización de una integración existente.",
"ar": "<b>هام:</b> راجع <a>الوثائق</a> لمزيد من المعلومات حول تكوين تكامل مساحة العمل أو تحديث تكامل موجود.",
"fr": "<b>Important :</b> Consultez la <a>documentation</a> pour plus d'informations sur la configuration de l'intégration de l'espace de travail ou la mise à jour d'une intégration existante.",
"tr": "<b>Önemli:</b> Çalışma alanı entegrasyonunu yapılandırma veya mevcut bir entegrasyonu güncelleme hakkında daha fazla bilgi için <a>belgelere</a> bakın.",
"de": "<b>Wichtig:</b> Weitere Informationen zur Konfiguration der Arbeitsbereichsintegration oder zur Aktualisierung einer bestehenden Integration finden Sie in der <a>Dokumentation</a>.",
"uk": "<b>Важливо:</b> Перегляньте <a>документацію</a> для отримання додаткової інформації про налаштування інтеграції робочого простору або оновлення існуючої інтеграції."
},
"PROJECT_MANAGEMENT$CONFIGURE_MODAL_TITLE": {
"en": "Configure {{platform}} Integration",
"ja": "{{platform}}統合を設定",
"zh-CN": "配置{{platform}}集成",
"zh-TW": "配置{{platform}}集成",
"ko-KR": "{{platform}} 통합 구성",
"no": "Konfigurer {{platform}}-integrasjon",
"it": "Configura integrazione {{platform}}",
"pt": "Configurar integração {{platform}}",
"es": "Configurar integración de {{platform}}",
"ar": "تكوين تكامل {{platform}}",
"fr": "Configurer l'intégration {{platform}}",
"tr": "{{platform}} Entegrasyonunu Yapılandır",
"de": "{{platform}}-Integration konfigurieren",
"uk": "Налаштувати інтеграцію {{platform}}"
},
"PROJECT_MANAGEMENT$IMPORTANT_WORKSPACE_INTEGRATION": {
"en": "<b>Important: </b>Make sure the workspace integration for your target workspace is already configured. Check the <a>documentation</a> for more information.",
"ja": "<b>重要: </b>対象のワークスペースのワークスペース統合がすでに設定されていることを確認してください。詳しくは<a>ドキュメント</a>をご覧ください。",
"zh-CN": "<b>重要提示:</b>请确保目标工作区的工作区集成已配置完毕。查看<a>文档</a>了解更多信息。",
"zh-TW": "<b>重要提示:</b>請確保目標工作區的工作區整合已設定完成。查看<a>文件</a>以了解更多資訊。",
"ko-KR": "<b>중요:</b>대상 작업 공간에 대한 작업 공간 통합이 이미 구성되어 있는지 확인하세요. 자세한 내용은 <a>설명서</a>를 참조하세요.",
"no": "<b>Viktig:</b>Sørg for at arbeidsområdeintegrasjonen for målarbeidsområdet ditt allerede er konfigurert. Se <a>dokumentasjonen</a> for mer informasjon.",
"it": "<b>Importante: </b>Assicurati che l'integrazione dell'area di lavoro per l'area di lavoro di destinazione sia già configurata. Consulta la <a>documentazione</a> per ulteriori informazioni.",
"pt": "<b>Importante:</b>Certifique-se de que a integração do workspace de destino já esteja configurada. Consulte a <a>documentação</a> para obter mais informações.",
"es": "Importante: Asegúrate de que la integración del espacio de trabajo de destino ya esté configurada. Consulta la documentación para obtener más información.",
"ar": "<b>هام: </b>تأكد من إعداد تكامل مساحة العمل لمساحة العمل المستهدفة. <a>راجع المستند لمزيد من المعلومات</a>.",
"fr": "<b>Important :</b>Assurez-vous que l'intégration de l'espace de travail cible est déjà configurée. Consultez la <a>documentation</a> pour plus d'informations.",
"tr": "<b>Önemli: </b>Hedef çalışma alanınız için çalışma alanı entegrasyonunun zaten yapılandırılmış olduğundan emin olun. Daha fazla bilgi için <a>belgelere</a> bakın.",
"de": "<b>Wichtig:</b>Stellen Sie sicher, dass die Arbeitsbereichsintegration für Ihren Zielarbeitsbereich bereits konfiguriert ist. Weitere Informationen finden Sie in der <a>Dokumentation</a>.",
"uk": "<b>Важливо: </b>Переконайтеся, що інтеграцію робочого простору для вашого цільового робочого простору вже налаштовано. Перегляньте <a>документацію</a> для отримання додаткової інформації."
},
"PROJECT_MANAGEMENT$WORKSPACE_NAME_VALIDATION_ERROR": {
"en": "Workspace name can only contain letters, numbers, hyphens, and underscores",
"ja": "ワークスペース名は文字、数字、ハイフン、アンダースコアのみ使用できます",
"zh-CN": "工作区名称只能包含字母、数字、连字符和下划线",
"zh-TW": "工作區名稱只能包含字母、數字、連字符和底線",
"ko-KR": "작업공간 이름은 문자, 숫자, 하이픈, 밑줄만 포함할 수 있습니다",
"no": "Arbeidsområdenavn kan bare inneholde bokstaver, tall, bindestrek og understrek",
"it": "Il nome dell'area di lavoro può contenere solo lettere, numeri, trattini e trattini bassi",
"pt": "O nome do workspace pode conter apenas letras, números, hífens e sublinhados",
"es": "El nombre del espacio de trabajo solo puede contener letras, números, guiones y guiones bajos",
"ar": "يمكن أن يحتوي اسم مساحة العمل على أحرف وأرقام وشرطات وشرطات سفلية فقط",
"fr": "Le nom de l'espace de travail ne peut contenir que des lettres, des chiffres, des tirets et des traits de soulignement",
"tr": "Çalışma alanı adı yalnızca harfler, sayılar, tire ve alt çizgi içerebilir",
"de": "Der Arbeitsbereichsname darf nur Buchstaben, Zahlen, Bindestriche und Unterstriche enthalten",
"uk": "Назва робочого простору може містити тільки літери, цифри, дефіси та підкреслення"
},
"PROJECT_MANAGEMENT$WEBHOOK_SECRET_NAME_VALIDATION_ERROR": {
"en": "Webhook secret cannot contain spaces",
"ja": "Webhookシークレットにはスペースを含めることはできません",
"zh-CN": "Webhook密钥不能包含空格",
"zh-TW": "Webhook密鑰不能包含空格",
"ko-KR": "웹훅 시크릿에는 공백을 포함할 수 없습니다",
"no": "Webhook-hemmelighet kan ikke inneholde mellomrom",
"it": "Il segreto del webhook non può contenere spazi",
"pt": "O segredo do webhook não pode conter espaços",
"es": "El secreto del webhook no puede contener espacios",
"ar": "لا يمكن أن يحتوي سر الويب هوك على مسافات",
"fr": "Le secret du webhook ne peut pas contenir d'espaces",
"tr": "Webhook sırrı boşluk içeremez",
"de": "Das Webhook-Geheimnis darf keine Leerzeichen enthalten",
"uk": "Секрет веб-хука не може містити пробіли"
},
"PROJECT_MANAGEMENT$SVC_ACC_EMAIL_VALIDATION_ERROR": {
"en": "Please enter a valid email address",
"ja": "有効なメールアドレスを入力してください",
"zh-CN": "请输入有效的电子邮件地址",
"zh-TW": "請輸入有效的電子郵件地址",
"ko-KR": "유효한 이메일 주소를 입력하세요",
"no": "Vennligst skriv inn en gyldig e-postadresse",
"it": "Inserisci un indirizzo email valido",
"pt": "Por favor, insira um endereço de email válido",
"es": "Por favor, introduce una dirección de correo electrónico válida",
"ar": "يرجى إدخال عنوان بريد إلكتروني صحيح",
"fr": "Veuillez saisir une adresse e-mail valide",
"tr": "Lütfen geçerli bir e-posta adresi girin",
"de": "Bitte geben Sie eine gültige E-Mail-Adresse ein",
"uk": "Будь ласка, введіть дійсну адресу електронної пошти"
},
"PROJECT_MANAGEMENT$SVC_ACC_API_KEY_VALIDATION_ERROR": {
"en": "API key cannot contain spaces",
"ja": "APIキーにはスペースを含めることはできません",
"zh-CN": "API密钥不能包含空格",
"zh-TW": "API密鑰不能包含空格",
"ko-KR": "API 키에는 공백을 포함할 수 없습니다",
"no": "API-nøkkel kan ikke inneholde mellomrom",
"it": "La chiave API non può contenere spazi",
"pt": "A chave da API não pode conter espaços",
"es": "La clave API no puede contener espacios",
"ar": "لا يمكن أن يحتوي مفتاح API على مسافات",
"fr": "La clé API ne peut pas contenir d'espaces",
"tr": "API anahtarı boşluk içeremez",
"de": "Der API-Schlüssel darf keine Leerzeichen enthalten",
"uk": "Ключ API не може містити пробіли"
},
"MICROAGENT_MANAGEMENT$ERROR_LOADING_MICROAGENT_CONTENT": {
"en": "Error loading microagent content.",
"ja": "マイクロエージェントのコンテンツの読み込み中にエラーが発生しました。",

View File

@@ -186,6 +186,9 @@ export const handlers = [
FEATURE_FLAGS: {
ENABLE_BILLING: false,
HIDE_LLM_SETTINGS: mockSaas,
ENABLE_JIRA: false,
ENABLE_JIRA_DC: false,
ENABLE_LINEAR: false,
},
// Uncomment the following to test the maintenance banner
// MAINTENANCE: {

View File

@@ -18,6 +18,7 @@ import { retrieveAxiosErrorMessage } from "#/utils/retrieve-axios-error-message"
import { GitSettingInputsSkeleton } from "#/components/features/settings/git-settings/github-settings-inputs-skeleton";
import { useAddGitProviders } from "#/hooks/mutation/use-add-git-providers";
import { useUserProviders } from "#/hooks/use-user-providers";
import { ProjectManagementIntegration } from "#/components/features/settings/project-management/project-management-integration";
function GitSettingsScreen() {
const { t } = useTranslation();
@@ -110,6 +111,10 @@ function GitSettingsScreen() {
!gitlabHostInputHasValue &&
!bitbucketHostInputHasValue;
const shouldRenderExternalConfigureButtons = isSaas && config.APP_SLUG;
const shouldRenderProjectManagementIntegrations =
config?.FEATURE_FLAGS?.ENABLE_JIRA ||
config?.FEATURE_FLAGS?.ENABLE_JIRA_DC ||
config?.FEATURE_FLAGS?.ENABLE_LINEAR;
return (
<form
@@ -118,13 +123,35 @@ function GitSettingsScreen() {
className="flex flex-col h-full justify-between"
>
{!isLoading && (
<div className="p-9 flex flex-col gap-12">
<div className="p-9 flex flex-col">
{shouldRenderExternalConfigureButtons && !isLoading && (
<ConfigureGitHubRepositoriesAnchor slug={config.APP_SLUG!} />
<>
<div className="pb-1 flex flex-col">
<h3 className="text-xl font-medium text-white">
{t(I18nKey.SETTINGS$GITHUB)}
</h3>
<ConfigureGitHubRepositoriesAnchor slug={config.APP_SLUG!} />
</div>
<div className="w-1/2 border-b border-gray-200" />
</>
)}
{shouldRenderExternalConfigureButtons && !isLoading && (
<InstallSlackAppAnchor />
<>
<div className="pb-1 mt-6 flex flex-col">
<h3 className="text-xl font-medium text-white">
{t(I18nKey.SETTINGS$SLACK)}
</h3>
<InstallSlackAppAnchor />
</div>
<div className="w-1/2 border-b border-gray-200" />
</>
)}
{shouldRenderProjectManagementIntegrations && !isLoading && (
<div className="mt-6">
<ProjectManagementIntegration />
</div>
)}
{!isSaas && (

View File

@@ -0,0 +1,9 @@
You have received a follow-up message regarding issue {{ issue_key }}.
User's new message: {{ user_message }}
The latest issue title and description are included below as the user might have provided additional context or updates.
Compare these with the previous issue details to ensure you have the most current information.
Title: {{ issue_title }}
Description: {{ issue_description }}

View File

@@ -0,0 +1,14 @@
You have been assigned an issue from Jira. Your goal is to implement the requirements and resolve the issue.
Follow these instructions carefully:
1. Thoroughly analyze the issue description and Acceptance Criteria before writing any code.
2. If requirements are ambiguous or conflicting, request clarification from the user.
3. When creating branches and pull requests, adhere strictly to the following naming conventions using the issue key:
* Branch: `{{ issue_key }}-{{ description_in_kebab_case }}`
* Example: `FRE-24-improve-form-validations`
* PR Title: `[{{ issue_key }}] {{ pr_title }}`
* Example: `[FRE-24] Improve form validations`
4. If the repository already contains a style guides and PR templates, respect those.
5. Write clean, maintainable code that meets the requirements outlined in the issue.
6. Ensure that your code is well-documented and adheres to the project's coding standards.

View File

@@ -0,0 +1,7 @@
Given below are the issue title, description and the user's message.
Issue Key : {{ issue_key }}
Title: {{ issue_title }}
Description: {{ issue_description }}
User's message: {{ user_message }}

View File

@@ -0,0 +1,9 @@
You have received a follow-up message regarding issue {{ issue_key }}.
User's new message: {{ user_message }}
The latest issue title and description are included below as the user might have provided additional context or updates.
Compare these with the previous issue details to ensure you have the most current information.
Title: {{ issue_title }}
Description: {{ issue_description }}

View File

@@ -0,0 +1,14 @@
You have been assigned an issue from Jira. Your goal is to implement the requirements and resolve the issue.
Follow these instructions carefully:
1. Thoroughly analyze the issue description and Acceptance Criteria before writing any code.
2. If requirements are ambiguous or conflicting, request clarification from the user.
3. When creating branches and pull requests, adhere strictly to the following naming conventions using the issue key:
* Branch: `{{ issue_key }}-{{ description_in_kebab_case }}`
* Example: `FRE-24-improve-form-validations`
* PR Title: `[{{ issue_key }}] {{ pr_title }}`
* Example: `[FRE-24] Improve form validations`
4. If the repository already contains a style guides and PR templates, respect those.
5. Write clean, maintainable code that meets the requirements outlined in the issue.
6. Ensure that your code is well-documented and adheres to the project's coding standards.

View File

@@ -0,0 +1,7 @@
Given below are the issue title, description and the user's message.
Issue Key : {{ issue_key }}
Title: {{ issue_title }}
Description: {{ issue_description }}
User's message: {{ user_message }}

View File

@@ -0,0 +1,9 @@
You have received a follow-up message regarding issue {{ issue_key }}.
User's new message: {{ user_message }}
The latest issue title and description are included below as the user might have provided additional context or updates.
Compare these with the previous issue details to ensure you have the most current information.
Title: {{ issue_title }}
Description: {{ issue_description }}

View File

@@ -0,0 +1,14 @@
You have been assigned an issue from Jira. Your goal is to implement the requirements and resolve the issue.
Follow these instructions carefully:
1. Thoroughly analyze the issue description and Acceptance Criteria before writing any code.
2. If requirements are ambiguous or conflicting, request clarification from the user.
3. When creating branches and pull requests, adhere strictly to the following naming conventions using the issue key:
* Branch: `{{ issue_key }}-{{ description_in_kebab_case }}`
* Example: `FRE-24-improve-form-validations`
* PR Title: `[{{ issue_key }}] {{ pr_title }}`
* Example: `[FRE-24] Improve form validations`
4. If the repository already contains a style guides and PR templates, respect those.
5. Write clean, maintainable code that meets the requirements outlined in the issue.
6. Ensure that your code is well-documented and adheres to the project's coding standards.

View File

@@ -0,0 +1,7 @@
Given below are the issue title, description and the user's message.
Issue Key : {{ issue_key }}
Title: {{ issue_title }}
Description: {{ issue_description }}
User's message: {{ user_message }}

View File

@@ -12,6 +12,9 @@ class ConversationTrigger(Enum):
REMOTE_API_KEY = 'openhands_api'
SLACK = 'slack'
MICROAGENT_MANAGEMENT = 'microagent_management'
JIRA = 'jira'
JIRA_DC = 'jira_dc'
LINEAR = 'linear'
@dataclass