diff --git a/web/packages/design/src/assets/images/icons/plugins.svg b/web/packages/design/src/assets/images/icons/plugins.svg
new file mode 100644
index 0000000000000..500602c500144
--- /dev/null
+++ b/web/packages/design/src/assets/images/icons/plugins.svg
@@ -0,0 +1,76 @@
+
diff --git a/web/packages/design/src/assets/images/icons/success.png b/web/packages/design/src/assets/images/icons/success.png
new file mode 100644
index 0000000000000..764c95362ff9d
Binary files /dev/null and b/web/packages/design/src/assets/images/icons/success.png differ
diff --git a/web/packages/teleport/src/Integrations/IntegrationList.tsx b/web/packages/teleport/src/Integrations/IntegrationList.tsx
index 9082a086c593f..0e414c1d73b80 100644
--- a/web/packages/teleport/src/Integrations/IntegrationList.tsx
+++ b/web/packages/teleport/src/Integrations/IntegrationList.tsx
@@ -22,8 +22,15 @@ import awsIcon from 'design/assets/images/icons/aws.svg';
import slackIcon from 'design/assets/images/icons/slack.svg';
import Table, { Cell } from 'design/DataTable';
import { MenuButton, MenuItem } from 'shared/components/MenuAction';
+import { ToolTipInfo } from 'shared/components/ToolTip';
-import type { Integration, Plugin } from 'teleport/services/integrations';
+import {
+ getStatusCodeDescription,
+ getStatusCodeTitle,
+ Integration,
+ IntegrationStatusCode,
+ Plugin,
+} from 'teleport/services/integrations';
type Props = {
list: IntegrationLike[];
@@ -75,12 +82,18 @@ export function IntegrationList(props: Props) {
const StatusCell = ({ item }: { item: IntegrationLike }) => {
const status = getStatus(item);
+ const statusDescription = getStatusCodeDescription(item.statusCode);
return (
|
- {item.statusCode}
+ {getStatusCodeTitle(item.statusCode)}
+ {statusDescription && (
+
+ {statusDescription}
+
+ )}
|
);
@@ -106,17 +119,20 @@ enum Status {
Error,
}
-function getStatus(item: IntegrationLike) {
+function getStatus(item: IntegrationLike): Status | null {
+ if (item.resourceType !== 'plugin') {
+ return Status.Success;
+ }
+
switch (item.statusCode) {
- case 'Running':
+ case IntegrationStatusCode.Unknown:
+ return null;
+ case IntegrationStatusCode.Running:
return Status.Success;
-
- case 'Unauthorized':
- case 'Unknown error':
- return Status.Error;
-
- case 'Bot not invited to channel':
+ case IntegrationStatusCode.SlackNotInChannel:
return Status.Warning;
+ default:
+ return Status.Error;
}
}
diff --git a/web/packages/teleport/src/Integrations/fixtures.ts b/web/packages/teleport/src/Integrations/fixtures.ts
index e29bc690fb4cd..24f8a35bbcb57 100644
--- a/web/packages/teleport/src/Integrations/fixtures.ts
+++ b/web/packages/teleport/src/Integrations/fixtures.ts
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+import { IntegrationStatusCode } from 'teleport/services/integrations';
+
import type { Plugin, Integration } from 'teleport/services/integrations';
export const plugins: Plugin[] = [
@@ -22,7 +24,7 @@ export const plugins: Plugin[] = [
name: 'slack-default',
details: `plugin running status`,
kind: 'slack',
- statusCode: 'Running',
+ statusCode: IntegrationStatusCode.Running,
spec: {},
},
{
@@ -30,7 +32,7 @@ export const plugins: Plugin[] = [
name: 'slack-secondary',
details: `plugin unknown status`,
kind: 'slack',
- statusCode: 'Unknown',
+ statusCode: IntegrationStatusCode.Unknown,
spec: {},
},
{
@@ -38,15 +40,15 @@ export const plugins: Plugin[] = [
name: 'acmeco-default',
details: `plugin unauthorized status`,
kind: 'acmeco' as any, // unknown plugin, should handle gracefuly
- statusCode: 'Unauthorized',
+ statusCode: IntegrationStatusCode.Unauthorized,
spec: {},
},
{
resourceType: 'plugin',
name: 'slack',
- details: 'plugin unknown error status',
+ details: 'plugin other error status',
kind: 'slack',
- statusCode: 'Unknown error',
+ statusCode: IntegrationStatusCode.OtherError,
spec: {},
},
{
@@ -54,7 +56,7 @@ export const plugins: Plugin[] = [
name: 'slack',
details: '',
kind: 'slack',
- statusCode: 'Bot not invited to channel',
+ statusCode: IntegrationStatusCode.SlackNotInChannel,
spec: {},
},
];
@@ -64,14 +66,14 @@ export const integrations: Integration[] = [
resourceType: 'integration',
name: 'aws',
kind: 'aws-oidc',
- statusCode: 'Running',
+ statusCode: IntegrationStatusCode.Running,
spec: { roleArn: '' },
},
{
resourceType: 'integration',
name: 'some-integration-name',
kind: '' as any,
- statusCode: 'Running',
+ statusCode: IntegrationStatusCode.Running,
spec: { roleArn: '' },
},
];
diff --git a/web/packages/teleport/src/services/integrations/integrations.ts b/web/packages/teleport/src/services/integrations/integrations.ts
index 894bd09f0533b..9e5e8fc8675d7 100644
--- a/web/packages/teleport/src/services/integrations/integrations.ts
+++ b/web/packages/teleport/src/services/integrations/integrations.ts
@@ -17,7 +17,7 @@
import api from 'teleport/services/api';
import cfg from 'teleport/config';
-import { Integration } from './types';
+import { Integration, IntegrationStatusCode } from './types';
export const integrationService = {
fetchIntegration(clusterId: string, name: string): Promise {
@@ -51,6 +51,6 @@ function makeIntegration(json: any): Integration {
// integration resources together. As discussed, the only
// supported status for integration is `Running` for now:
// https://github.com/gravitational/teleport/pull/22556#discussion_r1158674300
- statusCode: 'Running',
+ statusCode: IntegrationStatusCode.Running,
};
}
diff --git a/web/packages/teleport/src/services/integrations/types.ts b/web/packages/teleport/src/services/integrations/types.ts
index 3279d0e72b266..4973ea206f1c7 100644
--- a/web/packages/teleport/src/services/integrations/types.ts
+++ b/web/packages/teleport/src/services/integrations/types.ts
@@ -45,18 +45,44 @@ export type IntegrationSpecAwsOidc = {
roleArn: string;
};
-// IntegrationStatusCode must be in sync with the text values defined
-// in the backend as these are used to determine the status color:
-// https://github.com/gravitational/teleport.e/blob/1ebe50ce2fe608dc6dd24fef205fb9caaa216a46/lib/web/ui/plugins.go#L51
-export type IntegrationStatusCode =
- | 'Unknown'
- | 'Running'
- | 'Unknown error'
- | 'Unauthorized'
- | 'Bot not invited to channel';
+export enum IntegrationStatusCode {
+ Unknown = 0,
+ Running = 1,
+ OtherError = 2,
+ Unauthorized = 3,
+ SlackNotInChannel = 10,
+}
+
+export function getStatusCodeTitle(code: IntegrationStatusCode): string {
+ switch (code) {
+ case IntegrationStatusCode.Unknown:
+ return 'Unknown';
+ case IntegrationStatusCode.Running:
+ return 'Running';
+ case IntegrationStatusCode.Unauthorized:
+ return 'Unauthorized';
+ case IntegrationStatusCode.SlackNotInChannel:
+ return 'Bot not invited to channel';
+ default:
+ return 'Unknown error';
+ }
+}
+
+export function getStatusCodeDescription(
+ code: IntegrationStatusCode
+): string | null {
+ switch (code) {
+ case IntegrationStatusCode.Unauthorized:
+ return 'The integration was denied access. This could be a result of revoked authorization on the 3rd party provider. Try removing and re-connecting the integration.';
+
+ case IntegrationStatusCode.SlackNotInChannel:
+ return 'The Slack integration must be invited to the default channel in order to receive access request notifications.';
+
+ default:
+ return null;
+ }
+}
export type Plugin = Integration<'plugin', PluginKind, PluginSpec>;
-export type PluginSpec = {
- statusDescription?: string;
-};
+export type PluginSpec = Record; // currently no 'spec' fields exposed to the frontend
export type PluginKind = 'slack';