Restyle remaining bridge components

This commit is contained in:
Travis Ralston 2021-12-09 15:15:49 -07:00
parent d9006f4aec
commit f5236d842a
10 changed files with 227 additions and 321 deletions

View File

@ -1,50 +1,41 @@
<my-bridge-config [bridgeComponent]="this">
<ng-template #bridgeParamsTemplate>
<my-ibox [isCollapsible]="false">
<h5 class="my-ibox-title">
{{'Bridge to Jira' | translate}}
</h5>
<div class="my-ibox-content" *ngIf="loadingConnections">
<my-spinner></my-spinner>
<my-spinner *ngIf="loadingConnections"></my-spinner>
<div *ngIf="!loadingConnections">
<div *ngIf="isBridged">
<p>{{'This room is bridged to' | translate}} <a [href]="bridgedProjectUrl" rel="noopener" target="_blank">{{bridgedProjectUrlUnsafe}}</a></p>
<button type="button" class="element-btn element-btn-danger" [disabled]="isBusy" (click)="unbridgeRoom()">
{{'Unbridge' | translate}}
</button>
</div>
<div class="my-ibox-content" *ngIf="!loadingConnections">
<div *ngIf="isBridged">
<p>{{'This room is bridged to' | translate}} <a [href]="bridgedProjectUrl" rel="noopener" target="_blank">{{bridgedProjectUrlUnsafe}}</a></p>
<button type="button" class="btn btn-sm btn-danger" [disabled]="isBusy" (click)="unbridgeRoom()">
{{'Unbridge' | translate}}
</button>
</div>
<div *ngIf="!isBridged && authUrl">
<p>
{{'In order to bridge to Jira, you\'ll need to authorize the bridge to access your organization(s). Please click the button below to do so.' | translate}}
</p>
<a [href]="authUrl" rel="noopener" target="_blank" class="btn btn-lg btn-link">
<img src="/assets/img/avatars/jira.png" width="35" /> {{'Sign in with Jira' | translate}}
</a>
</div>
<div *ngIf="!isBridged && !authUrl">
<label class="label-block">
{{'Instance / Organization' | translate}}
<select class="form-control form-control-sm" [(ngModel)]="instance"
(change)="loadProjects()" [disabled]="isBusy">
<option *ngFor="let instance of instances" [ngValue]="instance">
{{ instance.name }} ({{ instance.url }})
</option>
</select>
</label>
<label class="label-block">
{{'Project' | translate}}
<select class="form-control form-control-sm" [(ngModel)]="project" [disabled]="isBusy">
<option *ngFor="let project of projects" [ngValue]="project">
{{ project.key }} ({{ project.name }})
</option>
</select>
</label>
<button type="button" class="btn btn-sm btn-primary" [disabled]="isBusy" (click)="bridgeRoom()">
Bridge
</button>
</div>
<div *ngIf="!isBridged && authUrl">
<p>
{{'In order to bridge to Jira, you\'ll need to authorize the bridge to access your organization(s). Please click the button below to do so.' | translate}}
</p>
<a [href]="authUrl" rel="noopener" target="_blank" class="btn btn-lg btn-link">
<img src="/assets/img/avatars/jira.png" width="35" /> {{'Sign in with Jira' | translate}}
</a>
</div>
</my-ibox>
<div *ngIf="!isBridged && !authUrl">
<my-field
label="{{'Instance' | translate}}"
[asSelect]="true"
[(value)]="instance"
[disabled]="isBusy"
(valueChange)="loadProjects()"
[selectOptions]="instanceOptions"
></my-field>
<my-field
label="{{'Project' | translate}}"
[asSelect]="true"
[(value)]="project"
[disabled]="isBusy"
[selectOptions]="projectOptions"
></my-field>
<button type="button" class="element-btn" [disabled]="isBusy" (click)="bridgeRoom()">
Bridge
</button>
</div>
</div>
</ng-template>
</my-bridge-config>

View File

@ -30,10 +30,10 @@ export class HookshotJiraBridgeConfigComponent extends BridgeComponent<HookshotC
public bridgedProjectUrlUnsafe: string;
public instances: FE_HookshotJiraInstance[] = [];
public instance: FE_HookshotJiraInstance;
public instance: string;
public projects: FE_HookshotJiraProject[] = [];
public project: FE_HookshotJiraProject;
public project: string;
private timerId: any;
@ -41,6 +41,14 @@ export class HookshotJiraBridgeConfigComponent extends BridgeComponent<HookshotC
super("hookshot_jira", translate);
}
public get instanceOptions(): {key: string, value: string}[] {
return this.instances.map(i => ({key: i.name, value: `${i.name} (${i.url})`}));
}
public get projectOptions(): {key: string, value: string}[] {
return this.projects.map(p => ({key: p.key, value: `${p.key} (${p.name})`}));
}
public ngOnInit() {
super.ngOnInit();
@ -52,7 +60,7 @@ export class HookshotJiraBridgeConfigComponent extends BridgeComponent<HookshotC
this.hookshot.getInstances().then(r => {
this.authUrl = null;
this.instances = r;
this.instance = this.instances[0];
this.instance = this.instances[0].name;
this.loadProjects();
if (this.timerId) {
@ -78,9 +86,9 @@ export class HookshotJiraBridgeConfigComponent extends BridgeComponent<HookshotC
public loadProjects() {
this.isBusy = true;
this.hookshot.getProjects(this.instance.name).then(projects => {
this.hookshot.getProjects(this.instance).then(projects => {
this.projects = projects;
this.project = this.projects[0];
this.project = this.projects[0].key;
if (this.isBridged) {
this.bridgedProjectUrlUnsafe = this.bridge.config.connections[0].config.url;
@ -120,8 +128,9 @@ export class HookshotJiraBridgeConfigComponent extends BridgeComponent<HookshotC
await this.scalar.setUserPowerLevel(this.roomId, this.bridge.config.botUserId, 50);
this.hookshot.bridgeRoom(this.roomId, this.instance.name, this.project.key).then(conn => {
this.hookshot.bridgeRoom(this.roomId, this.instance, this.project).then(conn => {
this.bridge.config.connections.push(conn);
this.loadProjects();
this.isBusy = false;
this.translate.get('Bridge requested').subscribe((res: string) => {
this.toaster.pop("success", res);

View File

@ -1,54 +1,29 @@
<my-bridge-config [bridgeComponent]="this">
<ng-template #bridgeParamsTemplate>
<my-ibox [isCollapsible]="true">
<h5 class="my-ibox-title">
{{'Add a new webhook' | translate}}
</h5>
<div class="my-ibox-content">
<label class="label-block">
{{'Webhook Name' | translate}}
<input title="webhook name" type="text" class="form-control form-control-sm"
[(ngModel)]="webhookName" [disabled]="isBusy">
</label>
<div style="margin-top: 25px">
<button type="button" class="btn btn-sm btn-primary" [disabled]="isBusy" (click)="newHook()">
{{'Create' | translate}}
</button>
</div>
<h3>{{'New webhook' | translate}}</h3>
<form (submit)="newHook()" novalidate name="addForm">
<my-field
label="{{'Webhook name' | translate}}"
[placeholder]="'My Webhook' | translate"
[(value)]="webhookName"
[disabled]="isBusy"
></my-field>
<button type="button" class="element-btn" [disabled]="isBusy" (click)="newHook()">
{{'Add webhook' | translate}}
</button>
</form>
<h3 style="margin-top: 36px">{{'Webhooks' | translate}}</h3>
<p *ngIf="newConfig.connections.length === 0">{{'No webhooks configured for this room.' | translate}}</p>
<details *ngFor="let hook of newConfig.connections trackById">
<summary>{{hook.config.name || ('No name' | translate)}}</summary>
URL: <a [href]="hook.config.url" target="_blank">{{ hook.config.url }}</a>
<div style="padding-bottom: 16px">
<button type="button" class="element-btn element-btn-danger" [disabled]="isBusy"
(click)="removeHook(hook)">
{{'Remove webhook' | translate}}
</button>
</div>
</my-ibox>
<my-ibox [isCollapsible]="true">
<h5 class="my-ibox-title">
Webhooks
</h5>
<div class="my-ibox-content">
<table class="table table-striped table-condensed table-bordered">
<thead>
<tr>
<th>Name</th>
<th>URL</th>
<th class="actions-col">{{'Actions' | translate}}</th>
</tr>
</thead>
<tbody>
<tr *ngIf="newConfig.connections.length === 0">
<td colspan="4">{{'No webhooks' | translate}}</td>
</tr>
<tr *ngFor="let hook of newConfig.connections">
<td *ngIf="hook.config.name">{{ hook.config.name }}</td>
<td *ngIf="!hook.config.name"><i>{{'No name' | translate}}</i></td>
<td class="webhook-url"><a [href]="hook.config.url" target="_blank">{{ hook.config.url }}</a></td>
<td class="actions-col">
<button type="button" class="btn btn-sm btn-outline-danger"
[disabled]="isBusy"
(click)="removeHook(hook)">
<i class="far fa-trash-alt"></i> {{'Delete' | translate}}
</button>
</td>
</tr>
</tbody>
</table>
</div>
</my-ibox>
</details>
</ng-template>
</my-bridge-config>

View File

@ -1,90 +1,54 @@
<my-bridge-config [bridgeComponent]="this">
<ng-template #bridgeParamsTemplate>
<my-ibox [isCollapsible]="true">
<h5 class="my-ibox-title">
{{'Add an IRC channel' | translate}}
</h5>
<div class="my-ibox-content">
<div class="alert alert-info">
{{'Bridging a channel requires authorization from a channel operator. When entering a channel below, a bot will join the channel to ensure it exists and has operators available.' | translate}}
</div>
<h3>{{'Add IRC channel' | translate}}</h3>
<div class="alert alert-info">
{{'Bridging a channel requires authorization from a channel operator. When entering a channel below, a bot will join the channel to ensure it exists and has operators available.' | translate}}
</div>
<div *ngIf="channelStep === 1">
<my-field
label="{{'Network' | translate}}"
[asSelect]="true"
[(value)]="networkId"
[disabled]="loadingOps"
[selectOptions]="getNetworks()"
></my-field>
<my-field
label="{{'Channel name (without leading #)' | translate}}"
placeholder="example"
[(value)]="channel"
[disabled]="loadingOps"
></my-field>
<button type="button" class="element-btn" [disabled]="loadingOps"
(click)="loadOps()">
{{'Next' | translate}}
</button>
</div>
<div *ngIf="channelStep === 1">
<label class="label-block">
{{'Network' | translate}}
<select class="form-control form-control-sm" [(ngModel)]="networkId" [disabled]="loadingOps">
<option *ngFor="let network of getNetworks()" [ngValue]="network.id">
{{ network.name }}
</option>
</select>
</label>
<label class="label-block">
{{'Channel Name' | translate}}
</label>
<div class="input-group input-group-sm">
<div class="input-group-addon">#</div>
<input title="channel" type="text" class="form-control form-control-sm" [(ngModel)]="channel"
[disabled]="loadingOps">
</div>
<div style="margin-top: 25px">
<button type="button" class="btn btn-sm btn-primary" [disabled]="loadingOps"
(click)="loadOps()">
{{'Next' | translate}}
</button>
</div>
</div>
<div *ngIf="channelStep === 2">
<p>{{'The person selected here will be asked to approve or deny the bridge request.' | translate}}</p>
<my-field
label="{{'Operator' | translate}}"
[asSelect]="true"
[(value)]="op"
[disabled]="requestingBridge"
[selectOptions]="ops"
></my-field>
<button type="button" class="element-btn" [disabled]="requestingBridge"
(click)="requestBridge()">
{{'Request bridge' | translate}}
</button>
</div>
<div *ngIf="channelStep === 2">
<label class="label-block">
{{'Operator' | translate}}
<span class="text-muted ">{{'The person selected here will be asked to approve or deny the bridge request.' | translate}}</span>
<select class="form-control form-control-sm" [(ngModel)]="op" [disabled]="requestingBridge">
<option *ngFor="let op of ops" [ngValue]="op">{{ op }}</option>
</select>
</label>
<div style="margin-top: 25px">
<button type="button" class="btn btn-sm btn-primary" [disabled]="requestingBridge"
(click)="requestBridge()">
{{'Request Bridge' | translate}}
</button>
</div>
</div>
<h3 style="margin-top: 36px">{{'Bridged channels' | translate}}</h3>
<p *ngIf="getChannels().length === 0">{{'No channels are bridged to this room.' | translate}}</p>
<details *ngFor="let channel of getChannels()">
<summary>{{channel.name + ' on ' + channel.networkName + (channel.pending ? ' (pending)' : '')}}</summary>
<div style="padding-bottom: 16px">
<button type="button" class="element-btn element-btn-danger" [disabled]="isUpdating || channel.pending"
(click)="removeChannel(channel)">
{{'Unbridge channel' | translate}}
</button>
</div>
</my-ibox>
<my-ibox [isCollapsible]="true">
<h5 class="my-ibox-title">
{{'IRC Networks' | translate}}
</h5>
<div class="my-ibox-content">
<table class="table table-striped table-condensed table-bordered">
<thead>
<tr>
<th>{{'Channel' | translate}}</th>
<th>{{'Network' | translate}}</th>
<th class="actions-col">{{'Actions' | translate}}</th>
</tr>
</thead>
<tbody>
<tr *ngIf="getChannels().length === 0">
<td colspan="3">{{'No bridged channels' | translate}}</td>
</tr>
<tr *ngFor="let channel of getChannels()">
<td>
{{ channel.name }}
<span *ngIf="channel.pending" class="text-muted">(pending)</span>
</td>
<td>{{ channel.networkName }}</td>
<td class="actions-col">
<button type="button" class="btn btn-sm btn-outline-danger"
[disabled]="isUpdating || channel.pending"
(click)="removeChannel(channel)">
<i class="far fa-trash-alt"></i> {{'Remove' | translate}}
</button>
</td>
</tr>
</tbody>
</table>
</div>
</my-ibox>
</details>
</ng-template>
</my-bridge-config>

View File

@ -41,17 +41,17 @@ export class IrcBridgeConfigComponent extends BridgeComponent<IrcConfig> {
}
private resetForm() {
this.networkId = this.getNetworks()[0].id;
this.networkId = this.getNetworks()[0].key;
this.channel = "";
this.ops = [];
this.channelStep = 1;
}
public getNetworks(): { id: string, name: string }[] {
public getNetworks(): { key: string, value: string }[] {
const ids = Object.keys(this.bridge.config.availableNetworks);
if (!this.networkId) setTimeout(() => this.networkId = ids[0], 0);
return ids.map(i => {
return {id: i, name: this.bridge.config.availableNetworks[i].name};
return {key: i, value: this.bridge.config.availableNetworks[i].name};
});
}

View File

@ -1,57 +1,48 @@
<my-bridge-config [bridgeComponent]="this">
<ng-template #bridgeParamsTemplate>
<my-ibox [isCollapsible]="false">
<h5 class="my-ibox-title">
{{'Bridge to Slack' | translate}}
</h5>
<div class="my-ibox-content" *ngIf="loadingTeams">
<my-spinner></my-spinner>
<my-spinner *ngIf="loadingTeams"></my-spinner>
<div *ngIf="!loadingTeams">
<div *ngIf="isBridged && bridge.config.link.isWebhook">
<p>{{'This room is bridged to Slack using webhooks. Webhook bridging is legacy and doesn\'t support as rich bridging as the new approach. It is recommended to re-create the bridge with the new process.' | translate}}</p>
<button type="button" class="element-btn element-btn-danger" [disabled]="isBusy" (click)="unbridgeRoom()">
{{'Unbridge' | translate}}
</button>
</div>
<div class="my-ibox-content" *ngIf="!loadingTeams">
<div *ngIf="isBridged && bridge.config.link.isWebhook">
{{'This room is bridged to Slack using webhooks. Webhook bridging is legacy and doesn\'t support as rich bridging as the new approach. It is recommended to re-create the bridge with the new process.' | translate}}
<br/>
<button type="button" class="btn btn-sm btn-danger" [disabled]="isBusy" (click)="unbridgeRoom()">
{{'Unbridge' | translate}}
</button>
</div>
<div *ngIf="isBridged && !bridge.config.link.isWebhook">
This room is bridged to "{{ bridge.config.link.channelName }}" on Slack.
<button type="button" class="btn btn-sm btn-danger" [disabled]="isBusy" (click)="unbridgeRoom()">
{{'Unbridge' | translate}}
</button>
</div>
<div *ngIf="!isBridged && needsAuth">
<p>
{{'In order to bridge Slack channels, you\'ll need to authorize the bridge to access your teams and channels. Please click the button below to do so.' | translate}}
</p>
<a [href]="authUrl" rel="noopener" target="_blank">
<img src="/assets/img/slack_auth_button.png" class="slack-auth-button" alt="sign in with slack"/>
</a>
</div>
<div *ngIf="!isBridged && !needsAuth">
<label class="label-block">
{{'Team' | translate}}
<select class="form-control form-control-sm" [(ngModel)]="teamId"
(change)="loadChannels()" [disabled]="isBusy">
<option *ngFor="let team of teams" [ngValue]="team.id">
{{ team.name }}
</option>
</select>
</label>
<label class="label-block">
{{'Channel' | translate}}
<select class="form-control form-control-sm" [(ngModel)]="channelId" [disabled]="isBusy">
<option *ngFor="let channel of channels" [ngValue]="channel.id">
{{ channel.name }}
</option>
</select>
</label>
<button type="button" class="btn btn-sm btn-primary" [disabled]="isBusy" (click)="bridgeRoom()">
Bridge
</button>
</div>
<div *ngIf="isBridged && !bridge.config.link.isWebhook">
<p>{{'This room is bridged to ' | translate}}"{{ bridge.config.link.channelName }}"{{' on Slack.' | translate}}</p>
<button type="button" class="element-btn element-btn-danger" [disabled]="isBusy" (click)="unbridgeRoom()">
{{'Unbridge' | translate}}
</button>
</div>
</my-ibox>
<div *ngIf="!isBridged && needsAuth">
<p>
{{'In order to bridge Slack channels, you\'ll need to authorize the bridge to access your teams and channels. Please click the button below to do so.' | translate}}
</p>
<a [href]="authUrl" rel="noopener" target="_blank">
<img src="/assets/img/slack_auth_button.png" class="slack-auth-button" alt="sign in with slack"/>
</a>
</div>
<div *ngIf="!isBridged && !needsAuth">
<my-field
label="{{'Team' | translate}}"
[asSelect]="true"
[(value)]="teamId"
[disabled]="isBusy"
(valueChange)="loadChannels()"
[selectOptions]="teamOptions"
></my-field>
<my-field
label="{{'Channel' | translate}}"
[asSelect]="true"
[(value)]="channelId"
[disabled]="isBusy"
[selectOptions]="channelOptions"
></my-field>
<button type="button" class="element-btn" [disabled]="isBusy" (click)="bridgeRoom()">
{{'Bridge' | translate}}
</button>
</div>
</div>
</ng-template>
</my-bridge-config>

View File

@ -33,6 +33,14 @@ export class SlackBridgeConfigComponent extends BridgeComponent<SlackConfig> imp
this.translate = translate;
}
public get teamOptions(): {key: string, value: string}[] {
return this.teams.map(t => ({key: t.id, value: t.name}));
}
public get channelOptions(): {key: string, value: string}[] {
return this.channels.map(t => ({key: t.id, value: t.name}));
}
public ngOnInit() {
super.ngOnInit();

View File

@ -1,34 +1,29 @@
<my-bridge-config [bridgeComponent]="this">
<ng-template #bridgeParamsTemplate>
<my-ibox [isCollapsible]="false">
<h5 class="my-ibox-title">
{{'Bridge to Telegram' | translate}}
</h5>
<div class="my-ibox-content">
<div *ngIf="isBridged">
{{'This room is bridged to on Telegram' | translate}} "{{ chatName }}" (<code>{{ chatId }}</code>) {{'on Telegram' | translate}}.
<div *ngIf="canUnbridge">
<button type="button" class="btn btn-sm btn-danger" [disabled]="isUpdating"
(click)="unbridgeRoom()">
{{'Unbridge' | translate}}
</button>
</div>
<span *ngIf="!canUnbridge">
{{'You do not have the necessary permissions in this room to unbridge the channel.' | translate}}
</span>
</div>
<div *ngIf="!isBridged">
<label class="label-block">
Chat ID
<span class="text-muted">{{'After inviting' | translate}} <a href="https://t.me/{{ botUsername }}" target="_blank">@{{ botUsername }}</a> {{'to your Telegram chat, run the command' | translate}} <code>/id</code> {{'in the Telegram room to get the chat ID.' | translate}}</span>
<input title="chat ID" type="text" class="form-control form-control-sm col-md-3"
[(ngModel)]="chatId" [disabled]="isUpdating"/>
</label>
<button type="button" class="btn btn-sm btn-primary" [disabled]="isUpdating" (click)="bridgeRoom()">
Bridge
</button>
</div>
<div *ngIf="isBridged">
{{'This room is bridged to on Telegram' | translate}} "{{ chatName }}" (<code>{{ chatId }}</code>) {{'on Telegram' | translate}}.
<div *ngIf="canUnbridge">
<button type="button" class="element-btn element-btn-danger" [disabled]="isUpdating"
(click)="unbridgeRoom()">
{{'Unbridge' | translate}}
</button>
</div>
</my-ibox>
<span *ngIf="!canUnbridge">
{{'You do not have the necessary permissions in this room to unbridge the channel.' | translate}}
</span>
</div>
<div *ngIf="!isBridged">
<p class="text-muted">{{'After inviting' | translate}} <a href="https://t.me/{{ botUsername }}" target="_blank">@{{ botUsername }}</a> {{'to your Telegram chat, run the command' | translate}} <code>/id</code> {{'in the Telegram room to get the chat ID.' | translate}}</p>
<my-field
label="{{'Chat ID' | translate}}"
type="number"
[(value)]="chatId"
[disabled]="isUpdating"
></my-field>
<button type="button" class="element-btn" [disabled]="isUpdating" (click)="bridgeRoom()">
Bridge
</button>
</div>
</ng-template>
</my-bridge-config>

View File

@ -58,19 +58,19 @@ export class TelegramBridgeConfigComponent extends BridgeComponent<TelegramConfi
return this.bridge.config.portalInfo ? this.bridge.config.portalInfo.chatName : null;
}
public get chatId(): number {
return this.bridge.config.portalInfo ? this.bridge.config.portalInfo.chatId : 0;
public get chatId(): string {
return `${this.bridge.config.portalInfo ? this.bridge.config.portalInfo.chatId : 0}`;
}
public set chatId(n: number) {
public set chatId(n: string) {
if (!this.bridge.config.portalInfo) this.bridge.config.portalInfo = {
chatId: n,
chatId: Number(n),
chatName: null,
canUnbridge: false,
bridged: false,
roomId: this.roomId,
};
else this.bridge.config.portalInfo.chatId = n;
else this.bridge.config.portalInfo.chatId = Number(n);
}
public bridgeRoom(): void {

View File

@ -1,56 +1,29 @@
<my-bridge-config [bridgeComponent]="this">
<ng-template #bridgeParamsTemplate>
<my-ibox [isCollapsible]="true">
<h5 class="my-ibox-title">
{{'Add a new webhook' | translate}}
</h5>
<div class="my-ibox-content">
<label class="label-block">
{{'Webhook Name' | translate}}
<input title="webhook name" type="text" class="form-control form-control-sm"
[(ngModel)]="webhookName" [disabled]="isBusy">
</label>
<div style="margin-top: 25px">
<button type="button" class="btn btn-sm btn-primary" [disabled]="isBusy" (click)="newHook()">
{{'Create' | translate}}
</button>
</div>
<h3>{{'New webhook' | translate}}</h3>
<form (submit)="newHook()" novalidate name="addForm">
<my-field
label="{{'Webhook name' | translate}}"
[placeholder]="'My Webhook' | translate"
[(value)]="webhookName"
[disabled]="isBusy"
></my-field>
<button type="button" class="element-btn" [disabled]="isBusy" (click)="newHook()">
{{'Add webhook' | translate}}
</button>
</form>
<h3 style="margin-top: 36px">{{'Webhooks' | translate}}</h3>
<p *ngIf="newConfig.webhooks.length === 0">{{'No webhooks configured for this room.' | translate}}</p>
<details *ngFor="let hook of newConfig.webhooks trackById">
<summary>{{hook.label || ('No name' | translate)}}</summary>
URL: <a [href]="hook.url" target="_blank">{{ hook.url }}</a>
<div style="padding-bottom: 16px">
<button type="button" class="element-btn element-btn-danger" [disabled]="isBusy"
(click)="removeHook(hook)">
{{'Remove webhook' | translate}}
</button>
</div>
</my-ibox>
<my-ibox [isCollapsible]="true">
<h5 class="my-ibox-title">
Webhooks
</h5>
<div class="my-ibox-content">
<table class="table table-striped table-condensed table-bordered">
<thead>
<tr>
<th>Name</th>
<th>{{'Type' | translate}}</th>
<th>URL</th>
<th class="actions-col">{{'Actions' | translate}}</th>
</tr>
</thead>
<tbody>
<tr *ngIf="newConfig.webhooks.length === 0">
<td colspan="4">{{'No webhooks' | translate}}</td>
</tr>
<tr *ngFor="let hook of newConfig.webhooks">
<td *ngIf="hook.label">{{ hook.label }}</td>
<td *ngIf="!hook.label"><i>{{'No name' | translate}}</i></td>
<td>{{ hook.type }}</td>
<td class="webhook-url"><a [href]="hook.url" target="_blank">{{ hook.url }}</a></td>
<td class="actions-col">
<button type="button" class="btn btn-sm btn-outline-danger"
[disabled]="isBusy"
(click)="removeHook(hook)">
<i class="far fa-trash-alt"></i> {{'Delete' | translate}}
</button>
</td>
</tr>
</tbody>
</table>
</div>
</my-ibox>
</details>
</ng-template>
</my-bridge-config>