diff --git a/apps/web/src/app/admin-console/organizations/integrations/integrations.component.ts b/apps/web/src/app/admin-console/organizations/integrations/integrations.component.ts
index e6a62b1db73..5c6fa72c311 100644
--- a/apps/web/src/app/admin-console/organizations/integrations/integrations.component.ts
+++ b/apps/web/src/app/admin-console/organizations/integrations/integrations.component.ts
@@ -228,6 +228,15 @@ export class AdminConsoleIntegrationsComponent implements OnInit {
image: "../../../../../../../images/integrations/logo-microsoft-intune-color.svg",
type: IntegrationType.DEVICE,
},
+ {
+ name: "Crowdstrike",
+ linkURL: "",
+ image: "../../../../../../../images/integrations/logo-crowdstrike-black.svg",
+ type: IntegrationType.EVENT,
+ description: "crowdstrikeEventIntegrationDesc",
+ isConnected: false,
+ canSetupConnection: true,
+ },
];
}
diff --git a/apps/web/src/app/admin-console/organizations/shared/components/integrations/integration-card/integration-card.component.html b/apps/web/src/app/admin-console/organizations/shared/components/integrations/integration-card/integration-card.component.html
index e96fbef270c..d0afda4c0ae 100644
--- a/apps/web/src/app/admin-console/organizations/shared/components/integrations/integration-card/integration-card.component.html
+++ b/apps/web/src/app/admin-console/organizations/shared/components/integrations/integration-card/integration-card.component.html
@@ -17,8 +17,26 @@
-
{{ name }}
+
+ {{ name }}
+
+ {{ "on" | i18n }}
+ {{ "off" | i18n }}
+
+
+
{{ description }}
+
+
{
let component: IntegrationCardComponent;
let fixture: ComponentFixture;
+ const mockI18nService = mock();
const systemTheme$ = new BehaviorSubject(ThemeType.Light);
const usersPreferenceTheme$ = new BehaviorSubject(ThemeType.Light);
@@ -41,7 +42,7 @@ describe("IntegrationCardComponent", () => {
},
{
provide: I18nService,
- useValue: mock(),
+ useValue: mockI18nService,
},
],
}).compileComponents();
@@ -55,6 +56,7 @@ describe("IntegrationCardComponent", () => {
component.image = "test-image.png";
component.linkURL = "https://example.com/";
+ mockI18nService.t.mockImplementation((key) => key);
fixture.detectChanges();
});
@@ -67,7 +69,7 @@ describe("IntegrationCardComponent", () => {
it("renders card body", () => {
const name = fixture.nativeElement.querySelector("h3");
- expect(name.textContent).toBe("Integration Name");
+ expect(name.textContent).toContain("Integration Name");
});
it("assigns external rel attribute", () => {
@@ -182,4 +184,28 @@ describe("IntegrationCardComponent", () => {
});
});
});
+
+ describe("connected badge", () => {
+ it("shows connected badge when isConnected is true", () => {
+ component.isConnected = true;
+
+ expect(component.showConnectedBadge()).toBe(true);
+ });
+
+ it("does not show connected badge when isConnected is false", () => {
+ component.isConnected = false;
+ fixture.detectChanges();
+ const name = fixture.nativeElement.querySelector("h3 > span > span > span");
+
+ expect(name.textContent).toContain("off");
+ // when isConnected is true/false, the badge should be shown as on/off
+ // when isConnected is undefined, the badge should not be shown
+ expect(component.showConnectedBadge()).toBe(true);
+ });
+
+ it("does not show connected badge when isConnected is undefined", () => {
+ component.isConnected = undefined;
+ expect(component.showConnectedBadge()).toBe(false);
+ });
+ });
});
diff --git a/apps/web/src/app/admin-console/organizations/shared/components/integrations/integration-card/integration-card.component.ts b/apps/web/src/app/admin-console/organizations/shared/components/integrations/integration-card/integration-card.component.ts
index 20e4028e9df..4188579bef9 100644
--- a/apps/web/src/app/admin-console/organizations/shared/components/integrations/integration-card/integration-card.component.ts
+++ b/apps/web/src/app/admin-console/organizations/shared/components/integrations/integration-card/integration-card.component.ts
@@ -41,6 +41,9 @@ export class IntegrationCardComponent implements AfterViewInit, OnDestroy {
* @example "2024-12-31"
*/
@Input() newBadgeExpiration?: string;
+ @Input() description?: string;
+ @Input() isConnected?: boolean;
+ @Input() canSetupConnection?: boolean;
constructor(
private themeStateService: ThemeStateService,
@@ -93,4 +96,14 @@ export class IntegrationCardComponent implements AfterViewInit, OnDestroy {
return expirationDate > new Date();
}
+
+ showConnectedBadge(): boolean {
+ return this.isConnected !== undefined;
+ }
+
+ setupConnection(app: string) {
+ // This method can be used to handle the connection logic for the integration
+ // For example, it could open a modal or redirect to a setup page
+ this.isConnected = !this.isConnected; // Toggle connection state for demonstration
+ }
}
diff --git a/apps/web/src/app/admin-console/organizations/shared/components/integrations/integration-grid/integration-grid.component.html b/apps/web/src/app/admin-console/organizations/shared/components/integrations/integration-grid/integration-grid.component.html
index 4b4b3ac972b..b4eaff993f0 100644
--- a/apps/web/src/app/admin-console/organizations/shared/components/integrations/integration-grid/integration-grid.component.html
+++ b/apps/web/src/app/admin-console/organizations/shared/components/integrations/integration-grid/integration-grid.component.html
@@ -13,6 +13,9 @@
[imageDarkMode]="integration.imageDarkMode"
[externalURL]="integration.type === IntegrationType.SDK"
[newBadgeExpiration]="integration.newBadgeExpiration"
+ [description]="integration.description | i18n"
+ [isConnected]="integration.isConnected"
+ [canSetupConnection]="integration.canSetupConnection"
>
diff --git a/apps/web/src/app/admin-console/organizations/shared/components/integrations/models.ts b/apps/web/src/app/admin-console/organizations/shared/components/integrations/models.ts
index 765b1d44a2e..a231523b578 100644
--- a/apps/web/src/app/admin-console/organizations/shared/components/integrations/models.ts
+++ b/apps/web/src/app/admin-console/organizations/shared/components/integrations/models.ts
@@ -17,4 +17,7 @@ export type Integration = {
* @example "2024-12-31"
*/
newBadgeExpiration?: string;
+ description?: string;
+ isConnected?: boolean;
+ canSetupConnection?: boolean;
};
diff --git a/apps/web/src/images/integrations/logo-crowdstrike-black.svg b/apps/web/src/images/integrations/logo-crowdstrike-black.svg
new file mode 100644
index 00000000000..25875d705cb
--- /dev/null
+++ b/apps/web/src/images/integrations/logo-crowdstrike-black.svg
@@ -0,0 +1,22 @@
+
diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json
index d5ded3c75ea..5f6570a24d9 100644
--- a/apps/web/src/locales/en/messages.json
+++ b/apps/web/src/locales/en/messages.json
@@ -9469,6 +9469,9 @@
"deviceManagementDesc": {
"message": "Configure device management for Bitwarden using the implementation guide for your platform."
},
+ "crowdstrikeEventIntegrationDesc": {
+ "message": "Send event data to your Logscale instance"
+ },
"deviceIdMissing": {
"message": "Device ID is missing"
},
@@ -9484,6 +9487,15 @@
"reopenLinkOnDesktop": {
"message": "Reopen this link from your email on a desktop."
},
+ "connectIntegrationButtonDesc": {
+ "message": "Connect $INTEGRATION$",
+ "placeholders": {
+ "integration": {
+ "content": "$1",
+ "example": "Crowdstrike"
+ }
+ }
+ },
"integrationCardTooltip": {
"message": "Launch $INTEGRATION$ implementation guide.",
"placeholders": {