mirror of
https://github.com/bitwarden/browser
synced 2025-12-15 15:53:27 +00:00
[PM-12273] Admin Console Integration Page (#11883)
* Integration page initial implementation * replace placeholder integrations * fix linting and tests * fix locales * update locale * Change logos, add link to SCIM page * refactor to standalone components, add integration filtering pipe * refactor modules and imports. Remove hyperlink text from integration card template * refactor i18n usage to be more generic * Add storybooks * fix tests * minify svgs, include spec files in TS config, fix stories * Update apps/web/src/locales/en/messages.json Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com> * fix imports, add static dir for stories * Add darkmode svgs for integrations * hide nav link for non enterprise orgs * add router guard * change rxjs selector * Remove tailwind class causing style issues --------- Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com>
This commit is contained in:
@@ -1,6 +1,11 @@
|
||||
<app-header></app-header>
|
||||
|
||||
<p bitTypography="body1">{{ "scimDescription" | i18n }}</p>
|
||||
<p bitTypography="body1">
|
||||
{{ "scimIntegrationDescription" | i18n }}
|
||||
<a bitLink target="_blank" href="https://bitwarden.com/help/about-scim/"
|
||||
><i class="bwi bwi-question-circle"></i
|
||||
></a>
|
||||
</p>
|
||||
|
||||
<div *ngIf="loading">
|
||||
<i
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
<div
|
||||
class="tw-block tw-h-full tw-overflow-hidden tw-rounded tw-border tw-border-solid tw-border-secondary-600 tw-relative tw-transition-all xl:tw-w-64 hover:tw-scale-105 focus-within:tw-outline-none focus-within:tw-ring focus-within:tw-ring-primary-700 focus-within:tw-ring-offset-2"
|
||||
>
|
||||
<div
|
||||
class="tw-flex tw-h-36 tw-bg-secondary-100 tw-items-center tw-justify-center tw-py-2 tw-px-6 lg:tw-py-4 lg:tw-px-12"
|
||||
>
|
||||
<div class="tw-flex tw-items-center tw-justify-center tw-h-28 tw-w-28 lg:tw-w-40">
|
||||
<img
|
||||
#imageEle
|
||||
[src]="image"
|
||||
alt=""
|
||||
class="tw-block tw-mx-auto tw-h-auto tw-max-w-full tw-max-h-full"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tw-p-5">
|
||||
<h3 class="tw-mb-4 tw-text-lg tw-font-semibold">{{ name }}</h3>
|
||||
<a
|
||||
class="tw-block tw-mb-0 tw-font-bold hover:tw-no-underline focus:tw-outline-none after:tw-content-[''] after:tw-block after:tw-absolute after:tw-w-full after:tw-h-full after:tw-left-0 after:tw-top-0"
|
||||
[href]="linkURL"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
{{ linkText }}
|
||||
</a>
|
||||
<span *ngIf="showNewBadge()" bitBadge class="tw-mt-3" variant="secondary">
|
||||
{{ "new" | i18n }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,174 +0,0 @@
|
||||
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
||||
import { BehaviorSubject } from "rxjs";
|
||||
|
||||
import { SYSTEM_THEME_OBSERVABLE } from "../../../../../../../libs/angular/src/services/injection-tokens";
|
||||
import { ThemeType } from "../../../../../../../libs/common/src/platform/enums";
|
||||
import { ThemeStateService } from "../../../../../../../libs/common/src/platform/theming/theme-state.service";
|
||||
|
||||
import { IntegrationCardComponent } from "./integration-card.component";
|
||||
|
||||
describe("IntegrationCardComponent", () => {
|
||||
let component: IntegrationCardComponent;
|
||||
let fixture: ComponentFixture<IntegrationCardComponent>;
|
||||
|
||||
const systemTheme$ = new BehaviorSubject<ThemeType>(ThemeType.Light);
|
||||
const usersPreferenceTheme$ = new BehaviorSubject<ThemeType>(ThemeType.Light);
|
||||
|
||||
beforeEach(async () => {
|
||||
// reset system theme
|
||||
systemTheme$.next(ThemeType.Light);
|
||||
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [IntegrationCardComponent],
|
||||
providers: [
|
||||
{
|
||||
provide: ThemeStateService,
|
||||
useValue: { selectedTheme$: usersPreferenceTheme$ },
|
||||
},
|
||||
{
|
||||
provide: SYSTEM_THEME_OBSERVABLE,
|
||||
useValue: systemTheme$,
|
||||
},
|
||||
],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(IntegrationCardComponent);
|
||||
component = fixture.componentInstance;
|
||||
|
||||
component.name = "Integration Name";
|
||||
component.image = "test-image.png";
|
||||
component.linkText = "Get started with integration";
|
||||
component.linkURL = "https://example.com/";
|
||||
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it("assigns link href", () => {
|
||||
const link = fixture.nativeElement.querySelector("a");
|
||||
|
||||
expect(link.href).toBe("https://example.com/");
|
||||
});
|
||||
|
||||
it("renders card body", () => {
|
||||
const name = fixture.nativeElement.querySelector("h3");
|
||||
const link = fixture.nativeElement.querySelector("a");
|
||||
|
||||
expect(name.textContent).toBe("Integration Name");
|
||||
expect(link.textContent.trim()).toBe("Get started with integration");
|
||||
});
|
||||
|
||||
it("assigns external rel attribute", () => {
|
||||
component.externalURL = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
const link = fixture.nativeElement.querySelector("a");
|
||||
|
||||
expect(link.rel).toBe("noopener noreferrer");
|
||||
});
|
||||
|
||||
describe("new badge", () => {
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers();
|
||||
jest.setSystemTime(new Date("2023-09-01"));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it("shows when expiration is in the future", () => {
|
||||
component.newBadgeExpiration = "2023-09-02";
|
||||
expect(component.showNewBadge()).toBe(true);
|
||||
});
|
||||
|
||||
it("does not show when expiration is not set", () => {
|
||||
expect(component.showNewBadge()).toBe(false);
|
||||
});
|
||||
|
||||
it("does not show when expiration is in the past", () => {
|
||||
component.newBadgeExpiration = "2023-08-31";
|
||||
expect(component.showNewBadge()).toBe(false);
|
||||
});
|
||||
|
||||
it("does not show when expiration is today", () => {
|
||||
component.newBadgeExpiration = "2023-09-01";
|
||||
expect(component.showNewBadge()).toBe(false);
|
||||
});
|
||||
|
||||
it("does not show when expiration is invalid", () => {
|
||||
component.newBadgeExpiration = "not-a-date";
|
||||
expect(component.showNewBadge()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("imageDarkMode", () => {
|
||||
it("ignores theme changes when darkModeImage is not set", () => {
|
||||
systemTheme$.next(ThemeType.Dark);
|
||||
usersPreferenceTheme$.next(ThemeType.Dark);
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.imageEle.nativeElement.src).toContain("test-image.png");
|
||||
});
|
||||
|
||||
describe("user prefers the system theme", () => {
|
||||
beforeEach(() => {
|
||||
component.imageDarkMode = "test-image-dark.png";
|
||||
});
|
||||
|
||||
it("sets image src to imageDarkMode", () => {
|
||||
usersPreferenceTheme$.next(ThemeType.System);
|
||||
systemTheme$.next(ThemeType.Dark);
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.imageEle.nativeElement.src).toContain("test-image-dark.png");
|
||||
});
|
||||
|
||||
it("sets image src to light mode image", () => {
|
||||
component.imageEle.nativeElement.src = "test-image-dark.png";
|
||||
|
||||
usersPreferenceTheme$.next(ThemeType.System);
|
||||
systemTheme$.next(ThemeType.Light);
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.imageEle.nativeElement.src).toContain("test-image.png");
|
||||
});
|
||||
});
|
||||
|
||||
describe("user prefers dark mode", () => {
|
||||
beforeEach(() => {
|
||||
component.imageDarkMode = "test-image-dark.png";
|
||||
});
|
||||
|
||||
it("updates image to dark mode", () => {
|
||||
systemTheme$.next(ThemeType.Light); // system theme shouldn't matter
|
||||
usersPreferenceTheme$.next(ThemeType.Dark);
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.imageEle.nativeElement.src).toContain("test-image-dark.png");
|
||||
});
|
||||
});
|
||||
|
||||
describe("user prefers light mode", () => {
|
||||
beforeEach(() => {
|
||||
component.imageDarkMode = "test-image-dark.png";
|
||||
});
|
||||
|
||||
it("updates image to light mode", () => {
|
||||
component.imageEle.nativeElement.src = "test-image-dark.png";
|
||||
|
||||
systemTheme$.next(ThemeType.Dark); // system theme shouldn't matter
|
||||
usersPreferenceTheme$.next(ThemeType.Light);
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.imageEle.nativeElement.src).toContain("test-image.png");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,93 +0,0 @@
|
||||
import {
|
||||
AfterViewInit,
|
||||
Component,
|
||||
ElementRef,
|
||||
Inject,
|
||||
Input,
|
||||
OnDestroy,
|
||||
ViewChild,
|
||||
} from "@angular/core";
|
||||
import { Observable, Subject, combineLatest, takeUntil } from "rxjs";
|
||||
|
||||
import { SYSTEM_THEME_OBSERVABLE } from "@bitwarden/angular/services/injection-tokens";
|
||||
import { ThemeType } from "@bitwarden/common/platform/enums";
|
||||
import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service";
|
||||
|
||||
@Component({
|
||||
selector: "sm-integration-card",
|
||||
templateUrl: "./integration-card.component.html",
|
||||
})
|
||||
export class IntegrationCardComponent implements AfterViewInit, OnDestroy {
|
||||
private destroyed$: Subject<void> = new Subject();
|
||||
@ViewChild("imageEle") imageEle: ElementRef<HTMLImageElement>;
|
||||
|
||||
@Input() name: string;
|
||||
@Input() image: string;
|
||||
@Input() imageDarkMode?: string;
|
||||
@Input() linkText: string;
|
||||
@Input() linkURL: string;
|
||||
|
||||
/** Adds relevant `rel` attribute to external links */
|
||||
@Input() externalURL?: boolean;
|
||||
|
||||
/**
|
||||
* Date of when the new badge should be hidden.
|
||||
* When omitted, the new badge is never shown.
|
||||
*
|
||||
* @example "2024-12-31"
|
||||
*/
|
||||
@Input() newBadgeExpiration?: string;
|
||||
|
||||
constructor(
|
||||
private themeStateService: ThemeStateService,
|
||||
@Inject(SYSTEM_THEME_OBSERVABLE)
|
||||
private systemTheme$: Observable<ThemeType>,
|
||||
) {}
|
||||
|
||||
ngAfterViewInit() {
|
||||
combineLatest([this.themeStateService.selectedTheme$, this.systemTheme$])
|
||||
.pipe(takeUntil(this.destroyed$))
|
||||
.subscribe(([theme, systemTheme]) => {
|
||||
// When the card doesn't have a dark mode image, exit early
|
||||
if (!this.imageDarkMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (theme === ThemeType.System) {
|
||||
// When the user's preference is the system theme,
|
||||
// use the system theme to determine the image
|
||||
const prefersDarkMode =
|
||||
systemTheme === ThemeType.Dark || systemTheme === ThemeType.SolarizedDark;
|
||||
|
||||
this.imageEle.nativeElement.src = prefersDarkMode ? this.imageDarkMode : this.image;
|
||||
} else if (theme === ThemeType.Dark || theme === ThemeType.SolarizedDark) {
|
||||
// When the user's preference is dark mode, use the dark mode image
|
||||
this.imageEle.nativeElement.src = this.imageDarkMode;
|
||||
} else {
|
||||
// Otherwise use the light mode image
|
||||
this.imageEle.nativeElement.src = this.image;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.destroyed$.next();
|
||||
this.destroyed$.complete();
|
||||
}
|
||||
|
||||
/** Show the "new" badge when expiration is in the future */
|
||||
showNewBadge() {
|
||||
if (!this.newBadgeExpiration) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const expirationDate = new Date(this.newBadgeExpiration);
|
||||
|
||||
// Do not show the new badge for invalid dates
|
||||
if (isNaN(expirationDate.getTime())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return expirationDate > new Date();
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
<ul
|
||||
class="tw-inline-grid tw-grid-cols-3 tw-gap-6 tw-m-0 tw-p-0 tw-w-full tw-auto-cols-auto tw-list-none lg:tw-grid-cols-4 lg:tw-gap-10 lg:tw-w-auto"
|
||||
>
|
||||
<li *ngFor="let integration of integrations">
|
||||
<sm-integration-card
|
||||
[name]="integration.name"
|
||||
[linkText]="integration.linkText"
|
||||
[linkURL]="integration.linkURL"
|
||||
[image]="integration.image"
|
||||
[imageDarkMode]="integration.imageDarkMode"
|
||||
[externalURL]="integration.type === IntegrationType.SDK"
|
||||
[newBadgeExpiration]="integration.newBadgeExpiration"
|
||||
></sm-integration-card>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -1,81 +0,0 @@
|
||||
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
||||
import { By } from "@angular/platform-browser";
|
||||
import { mock } from "jest-mock-extended";
|
||||
import { of } from "rxjs";
|
||||
|
||||
import { SYSTEM_THEME_OBSERVABLE } from "../../../../../../../libs/angular/src/services/injection-tokens";
|
||||
import { IntegrationType } from "../../../../../../../libs/common/src/enums";
|
||||
import { ThemeType } from "../../../../../../../libs/common/src/platform/enums";
|
||||
import { ThemeStateService } from "../../../../../../../libs/common/src/platform/theming/theme-state.service";
|
||||
import { IntegrationCardComponent } from "../integration-card/integration-card.component";
|
||||
import { Integration } from "../models/integration";
|
||||
|
||||
import { IntegrationGridComponent } from "./integration-grid.component";
|
||||
|
||||
describe("IntegrationGridComponent", () => {
|
||||
let component: IntegrationGridComponent;
|
||||
let fixture: ComponentFixture<IntegrationGridComponent>;
|
||||
const integrations: Integration[] = [
|
||||
{
|
||||
name: "Integration 1",
|
||||
image: "test-image1.png",
|
||||
linkText: "Get started with integration 1",
|
||||
linkURL: "https://example.com/1",
|
||||
type: IntegrationType.Integration,
|
||||
},
|
||||
{
|
||||
name: "SDK 2",
|
||||
image: "test-image2.png",
|
||||
linkText: "View SDK 2",
|
||||
linkURL: "https://example.com/2",
|
||||
type: IntegrationType.SDK,
|
||||
},
|
||||
];
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [IntegrationGridComponent, IntegrationCardComponent],
|
||||
providers: [
|
||||
{
|
||||
provide: ThemeStateService,
|
||||
useValue: mock<ThemeStateService>(),
|
||||
},
|
||||
{
|
||||
provide: SYSTEM_THEME_OBSERVABLE,
|
||||
useValue: of(ThemeType.Light),
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
fixture = TestBed.createComponent(IntegrationGridComponent);
|
||||
component = fixture.componentInstance;
|
||||
component.integrations = integrations;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it("lists all integrations", () => {
|
||||
expect(component.integrations).toEqual(integrations);
|
||||
|
||||
const cards = fixture.debugElement.queryAll(By.directive(IntegrationCardComponent));
|
||||
|
||||
expect(cards.length).toBe(integrations.length);
|
||||
});
|
||||
|
||||
it("assigns the correct attributes to IntegrationCardComponent", () => {
|
||||
expect(component.integrations).toEqual(integrations);
|
||||
|
||||
const card = fixture.debugElement.queryAll(By.directive(IntegrationCardComponent))[1];
|
||||
|
||||
expect(card.componentInstance.name).toBe("SDK 2");
|
||||
expect(card.componentInstance.image).toBe("test-image2.png");
|
||||
expect(card.componentInstance.linkText).toBe("View SDK 2");
|
||||
expect(card.componentInstance.linkURL).toBe("https://example.com/2");
|
||||
});
|
||||
|
||||
it("assigns `externalURL` for SDKs", () => {
|
||||
const card = fixture.debugElement.queryAll(By.directive(IntegrationCardComponent));
|
||||
|
||||
expect(card[0].componentInstance.externalURL).toBe(false);
|
||||
expect(card[1].componentInstance.externalURL).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -1,15 +0,0 @@
|
||||
import { Component, Input } from "@angular/core";
|
||||
|
||||
import { IntegrationType } from "@bitwarden/common/enums";
|
||||
|
||||
import { Integration } from "../models/integration";
|
||||
|
||||
@Component({
|
||||
selector: "sm-integration-grid",
|
||||
templateUrl: "./integration-grid.component.html",
|
||||
})
|
||||
export class IntegrationGridComponent {
|
||||
@Input() integrations: Integration[];
|
||||
|
||||
protected IntegrationType = IntegrationType;
|
||||
}
|
||||
@@ -4,7 +4,11 @@
|
||||
|
||||
<section class="tw-mb-9">
|
||||
<p bitTypography="body1">{{ "integrationsDesc" | i18n }}</p>
|
||||
<sm-integration-grid [integrations]="integrations"></sm-integration-grid>
|
||||
<app-integration-grid
|
||||
[integrations]="integrations"
|
||||
[tooltipI18nKey]="'smIntegrationTooltip'"
|
||||
[ariaI18nKey]="'smIntegrationCardAriaLabel'"
|
||||
></app-integration-grid>
|
||||
</section>
|
||||
|
||||
<section class="tw-mb-9">
|
||||
@@ -12,5 +16,9 @@
|
||||
{{ "sdks" | i18n }}
|
||||
</h2>
|
||||
<p bitTypography="body1">{{ "sdksDesc" | i18n }}</p>
|
||||
<sm-integration-grid [integrations]="sdks"></sm-integration-grid>
|
||||
<app-integration-grid
|
||||
[integrations]="sdks"
|
||||
[tooltipI18nKey]="'smSdkTooltip'"
|
||||
[ariaI18nKey]="'smSdkAriaLabel'"
|
||||
></app-integration-grid>
|
||||
</section>
|
||||
|
||||
@@ -4,14 +4,17 @@ import { By } from "@angular/platform-browser";
|
||||
import { mock } from "jest-mock-extended";
|
||||
import { of } from "rxjs";
|
||||
|
||||
import { SharedModule } from "@bitwarden/components/src/shared";
|
||||
import {
|
||||
IntegrationCardComponent,
|
||||
IntegrationGridComponent,
|
||||
} from "@bitwarden/web-vault/app/shared";
|
||||
|
||||
import { SYSTEM_THEME_OBSERVABLE } from "../../../../../../libs/angular/src/services/injection-tokens";
|
||||
import { I18nService } from "../../../../../../libs/common/src/platform/abstractions/i18n.service";
|
||||
import { ThemeType } from "../../../../../../libs/common/src/platform/enums";
|
||||
import { ThemeStateService } from "../../../../../../libs/common/src/platform/theming/theme-state.service";
|
||||
import { I18nPipe } from "../../../../../../libs/components/src/shared/i18n.pipe";
|
||||
|
||||
import { IntegrationCardComponent } from "./integration-card/integration-card.component";
|
||||
import { IntegrationGridComponent } from "./integration-grid/integration-grid.component";
|
||||
import { IntegrationsComponent } from "./integrations.component";
|
||||
|
||||
@Component({
|
||||
@@ -31,18 +34,12 @@ describe("IntegrationsComponent", () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [
|
||||
IntegrationsComponent,
|
||||
IntegrationGridComponent,
|
||||
IntegrationCardComponent,
|
||||
MockHeaderComponent,
|
||||
MockNewMenuComponent,
|
||||
I18nPipe,
|
||||
],
|
||||
declarations: [IntegrationsComponent, MockHeaderComponent, MockNewMenuComponent],
|
||||
imports: [IntegrationGridComponent, IntegrationCardComponent, SharedModule],
|
||||
providers: [
|
||||
{
|
||||
provide: I18nService,
|
||||
useValue: mock<I18nService>({ t: (key) => key }),
|
||||
useValue: mock<I18nService>(),
|
||||
},
|
||||
{
|
||||
provide: ThemeStateService,
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import { IntegrationType } from "@bitwarden/common/enums";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
|
||||
import { Integration } from "./models/integration";
|
||||
import { Integration } from "@bitwarden/web-vault/app/shared";
|
||||
|
||||
@Component({
|
||||
selector: "sm-integrations",
|
||||
@@ -12,11 +10,10 @@ import { Integration } from "./models/integration";
|
||||
export class IntegrationsComponent {
|
||||
private integrationsAndSdks: Integration[] = [];
|
||||
|
||||
constructor(i18nService: I18nService) {
|
||||
constructor() {
|
||||
this.integrationsAndSdks = [
|
||||
{
|
||||
name: "Rust",
|
||||
linkText: i18nService.t("rustSDKRepo"),
|
||||
linkURL: "https://github.com/bitwarden/sdk",
|
||||
image: "../../../../../../../images/secrets-manager/sdks/rust.svg",
|
||||
imageDarkMode: "../../../../../../../images/secrets-manager/sdks/rust-white.svg",
|
||||
@@ -24,7 +21,6 @@ export class IntegrationsComponent {
|
||||
},
|
||||
{
|
||||
name: "GitHub Actions",
|
||||
linkText: i18nService.t("setUpGithubActions"),
|
||||
linkURL: "https://bitwarden.com/help/github-actions-integration/",
|
||||
image: "../../../../../../../images/secrets-manager/integrations/github.svg",
|
||||
imageDarkMode: "../../../../../../../images/secrets-manager/integrations/github-white.svg",
|
||||
@@ -32,7 +28,6 @@ export class IntegrationsComponent {
|
||||
},
|
||||
{
|
||||
name: "GitLab CI/CD",
|
||||
linkText: i18nService.t("setUpGitlabCICD"),
|
||||
linkURL: "https://bitwarden.com/help/gitlab-integration/",
|
||||
image: "../../../../../../../images/secrets-manager/integrations/gitlab.svg",
|
||||
imageDarkMode: "../../../../../../../images/secrets-manager/integrations/gitlab-white.svg",
|
||||
@@ -40,35 +35,30 @@ export class IntegrationsComponent {
|
||||
},
|
||||
{
|
||||
name: "Ansible",
|
||||
linkText: i18nService.t("setUpAnsible"),
|
||||
linkURL: "https://bitwarden.com/help/ansible-integration/",
|
||||
image: "../../../../../../../images/secrets-manager/integrations/ansible.svg",
|
||||
type: IntegrationType.Integration,
|
||||
},
|
||||
{
|
||||
name: "C#",
|
||||
linkText: i18nService.t("cSharpSDKRepo"),
|
||||
linkURL: "https://github.com/bitwarden/sdk/tree/main/languages/csharp",
|
||||
image: "../../../../../../../images/secrets-manager/sdks/c-sharp.svg",
|
||||
type: IntegrationType.SDK,
|
||||
},
|
||||
{
|
||||
name: "C++",
|
||||
linkText: i18nService.t("cPlusPlusSDKRepo"),
|
||||
linkURL: "https://github.com/bitwarden/sdk/tree/main/languages/cpp",
|
||||
image: "../../../../../../../images/secrets-manager/sdks/c-plus-plus.png",
|
||||
type: IntegrationType.SDK,
|
||||
},
|
||||
{
|
||||
name: "Go",
|
||||
linkText: i18nService.t("goSDKRepo"),
|
||||
linkURL: "https://github.com/bitwarden/sdk/tree/main/languages/go",
|
||||
image: "../../../../../../../images/secrets-manager/sdks/go.svg",
|
||||
type: IntegrationType.SDK,
|
||||
},
|
||||
{
|
||||
name: "Java",
|
||||
linkText: i18nService.t("javaSDKRepo"),
|
||||
linkURL: "https://github.com/bitwarden/sdk/tree/main/languages/java",
|
||||
image: "../../../../../../../images/secrets-manager/sdks/java.svg",
|
||||
imageDarkMode: "../../../../../../../images/secrets-manager/sdks/java-white.svg",
|
||||
@@ -76,35 +66,30 @@ export class IntegrationsComponent {
|
||||
},
|
||||
{
|
||||
name: "JS WebAssembly",
|
||||
linkText: i18nService.t("jsWebAssemblySDKRepo"),
|
||||
linkURL: "https://github.com/bitwarden/sdk/tree/main/languages/js",
|
||||
image: "../../../../../../../images/secrets-manager/sdks/wasm.svg",
|
||||
type: IntegrationType.SDK,
|
||||
},
|
||||
{
|
||||
name: "php",
|
||||
linkText: i18nService.t("phpSDKRepo"),
|
||||
linkURL: "https://github.com/bitwarden/sdk/tree/main/languages/php",
|
||||
image: "../../../../../../../images/secrets-manager/sdks/php.svg",
|
||||
type: IntegrationType.SDK,
|
||||
},
|
||||
{
|
||||
name: "Python",
|
||||
linkText: i18nService.t("pythonSDKRepo"),
|
||||
linkURL: "https://github.com/bitwarden/sdk/tree/main/languages/python",
|
||||
image: "../../../../../../../images/secrets-manager/sdks/python.svg",
|
||||
type: IntegrationType.SDK,
|
||||
},
|
||||
{
|
||||
name: "Ruby",
|
||||
linkText: i18nService.t("rubySDKRepo"),
|
||||
linkURL: "https://github.com/bitwarden/sdk/tree/main/languages/ruby",
|
||||
image: "../../../../../../../images/secrets-manager/sdks/ruby.png",
|
||||
type: IntegrationType.SDK,
|
||||
},
|
||||
{
|
||||
name: "Kubernetes Operator",
|
||||
linkText: i18nService.t("setUpKubernetes"),
|
||||
linkURL: "https://bitwarden.com/help/secrets-manager-kubernetes-operator/",
|
||||
image: "../../../../../../../images/secrets-manager/integrations/kubernetes.svg",
|
||||
type: IntegrationType.Integration,
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
import { NgModule } from "@angular/core";
|
||||
|
||||
import {
|
||||
IntegrationCardComponent,
|
||||
IntegrationGridComponent,
|
||||
} from "@bitwarden/web-vault/app/shared";
|
||||
|
||||
import { SecretsManagerSharedModule } from "../shared/sm-shared.module";
|
||||
|
||||
import { IntegrationCardComponent } from "./integration-card/integration-card.component";
|
||||
import { IntegrationGridComponent } from "./integration-grid/integration-grid.component";
|
||||
import { IntegrationsRoutingModule } from "./integrations-routing.module";
|
||||
import { IntegrationsComponent } from "./integrations.component";
|
||||
|
||||
@NgModule({
|
||||
imports: [SecretsManagerSharedModule, IntegrationsRoutingModule],
|
||||
declarations: [IntegrationsComponent, IntegrationGridComponent, IntegrationCardComponent],
|
||||
providers: [],
|
||||
imports: [
|
||||
SecretsManagerSharedModule,
|
||||
IntegrationsRoutingModule,
|
||||
IntegrationCardComponent,
|
||||
IntegrationGridComponent,
|
||||
],
|
||||
declarations: [IntegrationsComponent],
|
||||
})
|
||||
export class IntegrationsModule {}
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
import { IntegrationType } from "@bitwarden/common/enums";
|
||||
|
||||
/** Integration or SDK */
|
||||
export type Integration = {
|
||||
name: string;
|
||||
image: string;
|
||||
/**
|
||||
* Optional image shown in dark mode.
|
||||
*/
|
||||
imageDarkMode?: string;
|
||||
linkURL: string;
|
||||
linkText: string;
|
||||
type: IntegrationType;
|
||||
/**
|
||||
* Shows the "New" badge until the defined date.
|
||||
* When omitted, the badge is never shown.
|
||||
*
|
||||
* @example "2024-12-31"
|
||||
*/
|
||||
newBadgeExpiration?: string;
|
||||
};
|
||||
@@ -46,6 +46,7 @@
|
||||
"../../apps/web/src/**/*.spec.ts",
|
||||
"../../libs/common/src/platform/services/**/*.worker.ts",
|
||||
|
||||
"src/**/*.stories.ts"
|
||||
"src/**/*.stories.ts",
|
||||
"src/**/*.spec.ts"
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user