Front end for travis-ci self-service management

TODO:
* Backend (needs research)
* Webhook URL (probably proxy this)
This commit is contained in:
Travis Ralston 2018-03-28 22:18:33 -06:00
parent 71de7987c6
commit 606b6d9f56
5 changed files with 158 additions and 9 deletions

View File

@ -61,6 +61,7 @@ import { AdminNebImgurConfigComponent } from "./admin/neb/config/imgur/imgur.com
import { ConfigSimpleBotComponent } from "./configs/simple-bot/simple-bot.component"; import { ConfigSimpleBotComponent } from "./configs/simple-bot/simple-bot.component";
import { ConfigScreenComplexBotComponent } from "./configs/complex-bot/config-screen/config-screen.complex-bot.component"; import { ConfigScreenComplexBotComponent } from "./configs/complex-bot/config-screen/config-screen.complex-bot.component";
import { RssComplexBotConfigComponent } from "./configs/complex-bot/rss/rss.complex-bot.component"; import { RssComplexBotConfigComponent } from "./configs/complex-bot/rss/rss.complex-bot.component";
import { TravisCiComplexBotConfigComponent } from "./configs/complex-bot/travisci/travisci.complex-bot.component";
@NgModule({ @NgModule({
imports: [ imports: [
@ -116,6 +117,7 @@ import { RssComplexBotConfigComponent } from "./configs/complex-bot/rss/rss.comp
ConfigSimpleBotComponent, ConfigSimpleBotComponent,
ConfigScreenComplexBotComponent, ConfigScreenComplexBotComponent,
RssComplexBotConfigComponent, RssComplexBotConfigComponent,
TravisCiComplexBotConfigComponent,
// Vendor // Vendor
], ],

View File

@ -20,6 +20,7 @@ import { AdminNebComponent } from "./admin/neb/neb.component";
import { AdminEditNebComponent } from "./admin/neb/edit/edit.component"; import { AdminEditNebComponent } from "./admin/neb/edit/edit.component";
import { AdminAddSelfhostedNebComponent } from "./admin/neb/add-selfhosted/add-selfhosted.component"; import { AdminAddSelfhostedNebComponent } from "./admin/neb/add-selfhosted/add-selfhosted.component";
import { RssComplexBotConfigComponent } from "./configs/complex-bot/rss/rss.complex-bot.component"; import { RssComplexBotConfigComponent } from "./configs/complex-bot/rss/rss.complex-bot.component";
import { TravisCiComplexBotConfigComponent } from "./configs/complex-bot/travisci/travisci.complex-bot.component";
const routes: Routes = [ const routes: Routes = [
{path: "", component: HomeComponent}, {path: "", component: HomeComponent},
@ -64,7 +65,7 @@ const routes: Routes = [
path: "new/selfhosted", path: "new/selfhosted",
component: AdminAddSelfhostedNebComponent, component: AdminAddSelfhostedNebComponent,
data: {breadcrumb: "Add self-hosted go-neb", name: "Add self-hosted go-neb"}, data: {breadcrumb: "Add self-hosted go-neb", name: "Add self-hosted go-neb"},
} },
] ]
}, },
], ],
@ -75,37 +76,37 @@ const routes: Routes = [
{ {
path: "custom", path: "custom",
component: CustomWidgetConfigComponent, component: CustomWidgetConfigComponent,
data: {breadcrumb: "Custom Widgets", name: "Custom Widgets"} data: {breadcrumb: "Custom Widgets", name: "Custom Widgets"},
}, },
{ {
path: "etherpad", path: "etherpad",
component: EtherpadWidgetConfigComponent, component: EtherpadWidgetConfigComponent,
data: {breadcrumb: "Etherpad Widgets", name: "Etherpad Widgets"} data: {breadcrumb: "Etherpad Widgets", name: "Etherpad Widgets"},
}, },
{ {
path: "googlecalendar", path: "googlecalendar",
component: GoogleCalendarWidgetConfigComponent, component: GoogleCalendarWidgetConfigComponent,
data: {breadcrumb: "Google Calendar Widgets", name: "Google Calendar Widgets"} data: {breadcrumb: "Google Calendar Widgets", name: "Google Calendar Widgets"},
}, },
{ {
path: "googledocs", path: "googledocs",
component: GoogleDocsWidgetConfigComponent, component: GoogleDocsWidgetConfigComponent,
data: {breadcrumb: "Google Doc Widgets", name: "Google Doc Widgets"} data: {breadcrumb: "Google Doc Widgets", name: "Google Doc Widgets"},
}, },
{ {
path: "jitsi", path: "jitsi",
component: JitsiWidgetConfigComponent, component: JitsiWidgetConfigComponent,
data: {breadcrumb: "Jitsi Widgets", name: "Jitsi Widgets"} data: {breadcrumb: "Jitsi Widgets", name: "Jitsi Widgets"},
}, },
{ {
path: "twitch", path: "twitch",
component: TwitchWidgetConfigComponent, component: TwitchWidgetConfigComponent,
data: {breadcrumb: "Twitch Livestream Widgets", name: "Twitch Livestream Widgets"} data: {breadcrumb: "Twitch Livestream Widgets", name: "Twitch Livestream Widgets"},
}, },
{ {
path: "youtube", path: "youtube",
component: YoutubeWidgetConfigComponent, component: YoutubeWidgetConfigComponent,
data: {breadcrumb: "Youtube Video Widgets", name: "Youtube Video Widgets"} data: {breadcrumb: "Youtube Video Widgets", name: "Youtube Video Widgets"},
}, },
], ],
}, },
@ -115,7 +116,12 @@ const routes: Routes = [
{ {
path: "rss", path: "rss",
component: RssComplexBotConfigComponent, component: RssComplexBotConfigComponent,
data: {breadcrumb: "RSS Bot Configuration", name: "RSS Bot Configuration"} data: {breadcrumb: "RSS Bot Configuration", name: "RSS Bot Configuration"},
},
{
path: "travisci",
component: TravisCiComplexBotConfigComponent,
data: {breadcrumb: "Travis CI Configuration", name: "Travis CI Configuration"},
}, },
], ],
}, },

View File

@ -0,0 +1,62 @@
<my-complex-bot-config [botComponent]="this">
<ng-template #botParamsTemplate>
<my-ibox>
<h5 class="my-ibox-title">
Repositories
</h5>
<div class="my-ibox-content">
<form (submit)="interceptSave()" novalidate name="saveForm">
<table class="table table-striped table-condensed table-bordered">
<thead>
<tr>
<th>Repository</th>
<th>Template</th>
<th>Added by</th>
<th class="actions-col">Actions</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let repo of getRepos()">
<td>{{ repo.repoKey }}</td>
<td>
<textarea title="Repository Template" class="repo-template form-control" rows="3" (change)="repo.template = $event.target.value">{{ repo.template }}</textarea>
</td>
<td>{{ repo.addedByUserId }}</td>
<td class="actions-col">
<button type="button" class="btn btn-sm btn-outline-danger"
[disabled]="isUpdating || !repo.isSelf"
(click)="removeRepo(repo)">
<i class="far fa-trash-alt"></i> Remove
</button>
</td>
</tr>
<tr>
<td colspan="4">
<div class="input-group input-group-sm">
<input type="text" class="form-control"
[(ngModel)]="newRepoKey"
placeholder="matrix-org/synapse"
name="newRepoKey"
title="New repository name" />
<span class="input-group-btn">
<button type="button" class="btn btn-outline-success"
[disabled]="isUpdating"
(click)="addRepo()">
<i class="fa fa-plus"></i> Add
</button>
</span>
</div>
</td>
</tr>
</tbody>
</table>
<div style="margin-top: 25px">
<button type="submit" class="btn btn-sm btn-primary" [disabled]="isUpdating">
<i class="far fa-save"></i> Save
</button>
</div>
</form>
</div>
</my-ibox>
</ng-template>
</my-complex-bot-config>

View File

@ -0,0 +1,9 @@
.actions-col {
width: 120px;
text-align: center;
}
.repo-template {
white-space: nowrap; // Force scrollbars
width: 100%;
}

View File

@ -0,0 +1,70 @@
import { ComplexBotComponent } from "../complex-bot.component";
import { Component } from "@angular/core";
import { SessionStorage } from "../../../shared/SessionStorage";
interface TravisCiConfig {
webhookUrl: string; // TODO: Display webhook URL somewhere
repos: {
[repoKey: string]: { // "turt2live/matrix-dimension"
addedByUserId: string;
template: string;
};
};
}
interface LocalRepo {
repoKey: string;
template: string;
addedByUserId: string;
isSelf: boolean;
}
@Component({
templateUrl: "travisci.complex-bot.component.html",
styleUrls: ["travisci.complex-bot.component.scss"],
})
export class TravisCiComplexBotConfigComponent extends ComplexBotComponent<TravisCiConfig> {
public newRepoKey = "";
constructor() {
super("travisci");
}
public addRepo(): void {
if (!this.newRepoKey.trim()) {
this.toaster.pop('warning', 'Please enter a repository');
return;
}
this.newConfig.repos[this.newRepoKey] = {addedByUserId: SessionStorage.userId, template: "TODO: Default template"};
this.newRepoKey = "";
}
public getRepos(): LocalRepo[] {
if (!this.newConfig.repos) this.newConfig.repos = {};
return Object.keys(this.newConfig.repos).map(r => {
return {
repoKey: r,
template: this.newConfig.repos[r].template,
addedByUserId: this.newConfig.repos[r].addedByUserId,
isSelf: SessionStorage.userId === this.newConfig.repos[r].addedByUserId,
};
});
}
public removeRepo(repo: LocalRepo): void {
delete this.newConfig.repos[repo.repoKey];
}
public async interceptSave(): Promise<any> {
const memberEvent = await this.scalarClientApi.getMembershipState(this.roomId, this.bot.notificationUserId);
const isJoined = memberEvent && memberEvent.response && ["join", "invite"].indexOf(memberEvent.response.membership) !== -1;
if (!isJoined) {
await this.scalarClientApi.inviteUser(this.roomId, this.bot.notificationUserId);
}
super.save();
}
}