Compare commits
3 Commits
b1bac758e3
...
d44ec50a6a
Author | SHA1 | Date | |
---|---|---|---|
d44ec50a6a | |||
ea19375ad2 | |||
298d351e5d |
@ -91,7 +91,10 @@
|
|||||||
"buildTarget": "hermes-web-angular:build:development"
|
"buildTarget": "hermes-web-angular:build:development"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"defaultConfiguration": "development"
|
"defaultConfiguration": "development",
|
||||||
|
"options": {
|
||||||
|
"allowedHosts": ["*"]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"extract-i18n": {
|
"extract-i18n": {
|
||||||
"builder": "@angular-devkit/build-angular:extract-i18n"
|
"builder": "@angular-devkit/build-angular:extract-i18n"
|
||||||
|
28103
package-lock.json
generated
28103
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
98
package.json
98
package.json
@ -1,50 +1,50 @@
|
|||||||
{
|
{
|
||||||
"name": "hermes-web-angular",
|
"name": "hermes-web-angular",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"ng": "ng",
|
"ng": "ng",
|
||||||
"start": "ng serve -c production --host 0.0.0.0 --watch false",
|
"start": "ng serve -c production --host 0.0.0.0 --watch false",
|
||||||
"build": "ng build",
|
"build": "ng build",
|
||||||
"watch": "ng serve -c development --host 0.0.0.0",
|
"watch": "ng serve -c development --host 0.0.0.0 --disable-host-check",
|
||||||
"test": "ng test",
|
"test": "ng test",
|
||||||
"serve:ssr:hermes-web-angular": "node dist/hermes-web-angular/server/server.mjs"
|
"serve:ssr:hermes-web-angular": "node dist/hermes-web-angular/server/server.mjs"
|
||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/animations": "^19.0.5",
|
"@angular/animations": "^19.2.4",
|
||||||
"@angular/cdk": "^19.0.4",
|
"@angular/cdk": "^19.2.7",
|
||||||
"@angular/common": "^19.0.5",
|
"@angular/common": "^19.2.4",
|
||||||
"@angular/compiler": "^19.0.5",
|
"@angular/compiler": "^19.2.4",
|
||||||
"@angular/core": "^19.0.5",
|
"@angular/core": "^19.2.4",
|
||||||
"@angular/forms": "^19.0.5",
|
"@angular/forms": "^19.2.4",
|
||||||
"@angular/material": "^19.0.4",
|
"@angular/material": "^19.2.7",
|
||||||
"@angular/platform-browser": "^19.0.5",
|
"@angular/platform-browser": "^19.2.4",
|
||||||
"@angular/platform-browser-dynamic": "^19.0.5",
|
"@angular/platform-browser-dynamic": "^19.2.4",
|
||||||
"@angular/platform-server": "^19.0.5",
|
"@angular/platform-server": "^19.2.4",
|
||||||
"@angular/router": "^19.0.5",
|
"@angular/router": "^19.2.4",
|
||||||
"@angular/ssr": "^19.0.6",
|
"@angular/ssr": "^19.2.5",
|
||||||
"angular-oauth2-oidc": "^17.0.2",
|
"angular-oauth2-oidc": "^17.0.2",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"ngx-socket-io": "^4.7.0",
|
"ngx-socket-io": "^4.7.0",
|
||||||
"rxjs": "~7.8.0",
|
"rxjs": "~7.8.0",
|
||||||
"rxjs-websockets": "^9.0.0",
|
"rxjs-websockets": "^9.0.0",
|
||||||
"tslib": "^2.3.0",
|
"tslib": "^2.3.0",
|
||||||
"uuidv4": "^6.2.13",
|
"uuidv4": "^6.2.13",
|
||||||
"zone.js": "~0.15.0"
|
"zone.js": "~0.15.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@angular-devkit/build-angular": "^19.0.6",
|
"@angular-devkit/build-angular": "^19.2.5",
|
||||||
"@angular/cli": "^19.0.6",
|
"@angular/cli": "^19.2.5",
|
||||||
"@angular/compiler-cli": "^19.0.5",
|
"@angular/compiler-cli": "^19.2.4",
|
||||||
"@types/express": "^4.17.17",
|
"@types/express": "^4.17.17",
|
||||||
"@types/jasmine": "~5.1.0",
|
"@types/jasmine": "~5.1.0",
|
||||||
"@types/node": "^18.18.0",
|
"@types/node": "^18.18.0",
|
||||||
"jasmine-core": "~5.1.0",
|
"jasmine-core": "~5.1.0",
|
||||||
"karma": "~6.4.0",
|
"karma": "~6.4.0",
|
||||||
"karma-chrome-launcher": "~3.2.0",
|
"karma-chrome-launcher": "~3.2.0",
|
||||||
"karma-coverage": "~2.2.0",
|
"karma-coverage": "~2.2.0",
|
||||||
"karma-jasmine": "~5.1.0",
|
"karma-jasmine": "~5.1.0",
|
||||||
"karma-jasmine-html-reporter": "~2.1.0",
|
"karma-jasmine-html-reporter": "~2.1.0",
|
||||||
"typescript": "~5.6.3"
|
"typescript": "~5.6.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<body>
|
<content>
|
||||||
<mat-card>
|
<mat-card>
|
||||||
<mat-card-header>
|
<mat-card-header>
|
||||||
<mat-card-title-group>
|
<mat-card-title-group>
|
||||||
@ -108,18 +108,26 @@
|
|||||||
}
|
}
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
|
|
||||||
<mat-card-actions class="actions"
|
<mat-card-actions class="actions">
|
||||||
align="end">
|
|
||||||
@if (!isNew) {
|
@if (!isNew) {
|
||||||
<button mat-raised-button
|
<button mat-button
|
||||||
class="delete"
|
class="danger"
|
||||||
(click)="deleteAction(action)">Delete</button>
|
(click)="deleteAction(action)">
|
||||||
|
<mat-icon>delete</mat-icon>Delete
|
||||||
|
</button>
|
||||||
}
|
}
|
||||||
<button mat-raised-button
|
<button mat-button
|
||||||
(click)="dialogRef.close()">Cancel</button>
|
class="neutral"
|
||||||
<button mat-raised-button
|
disabled="{{waitForResponse}}"
|
||||||
|
(click)="dialogRef.close()">
|
||||||
|
<mat-icon>cancel</mat-icon>Cancel
|
||||||
|
</button>
|
||||||
|
<button mat-button
|
||||||
|
class="confirm"
|
||||||
disabled="{{!formsDirty || !formsValidity || waitForResponse}}"
|
disabled="{{!formsDirty || !formsValidity || waitForResponse}}"
|
||||||
(click)="save()">Save</button>
|
(click)="save()">
|
||||||
|
<mat-icon>save</mat-icon>Save
|
||||||
|
</button>
|
||||||
</mat-card-actions>
|
</mat-card-actions>
|
||||||
|
|
||||||
@if (responseError) {
|
@if (responseError) {
|
||||||
@ -128,4 +136,4 @@
|
|||||||
</mat-card-footer>
|
</mat-card-footer>
|
||||||
}
|
}
|
||||||
</mat-card>
|
</mat-card>
|
||||||
</body>
|
</content>
|
@ -9,12 +9,6 @@
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.actions {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.delete {
|
.delete {
|
||||||
background-color: #ea5151;
|
background-color: #ea5151;
|
||||||
color: #ba1a1a;
|
color: #ba1a1a;
|
||||||
|
@ -9,17 +9,19 @@ import { MatInputModule } from '@angular/material/input';
|
|||||||
import { MatSelectModule } from '@angular/material/select';
|
import { MatSelectModule } from '@angular/material/select';
|
||||||
import { createItemExistsInArrayValidator } from '../../shared/validators/item-exists-in-array';
|
import { createItemExistsInArrayValidator } from '../../shared/validators/item-exists-in-array';
|
||||||
import { HermesClientService } from '../../hermes-client.service';
|
import { HermesClientService } from '../../hermes-client.service';
|
||||||
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'action-item-edit',
|
selector: 'action-item-edit',
|
||||||
imports: [
|
imports: [
|
||||||
ReactiveFormsModule,
|
|
||||||
MatButtonModule,
|
MatButtonModule,
|
||||||
MatCardModule,
|
MatCardModule,
|
||||||
MatDialogModule,
|
MatDialogModule,
|
||||||
MatFormFieldModule,
|
MatFormFieldModule,
|
||||||
|
MatIconModule,
|
||||||
MatInputModule,
|
MatInputModule,
|
||||||
MatSelectModule
|
MatSelectModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
],
|
],
|
||||||
templateUrl: './action-item-edit.component.html',
|
templateUrl: './action-item-edit.component.html',
|
||||||
styleUrl: './action-item-edit.component.scss'
|
styleUrl: './action-item-edit.component.scss'
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
@use '@angular/material' as mat;
|
||||||
|
|
||||||
main {
|
main {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(1, 1fr);
|
grid-template-columns: repeat(1, 1fr);
|
||||||
@ -15,7 +17,7 @@ main {
|
|||||||
border: 1px solid grey;
|
border: 1px solid grey;
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
background-color: white;
|
background-color: transparent;
|
||||||
|
|
||||||
& span {
|
& span {
|
||||||
display: block;
|
display: block;
|
||||||
@ -27,7 +29,6 @@ main {
|
|||||||
|
|
||||||
& .subtitle {
|
& .subtitle {
|
||||||
font-size: smaller;
|
font-size: smaller;
|
||||||
color: lightgrey;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,19 +7,28 @@ import { MatIconModule } from '@angular/material/icon';
|
|||||||
import { MatDialog } from '@angular/material/dialog';
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
import { ActionItemEditComponent } from '../action-item-edit/action-item-edit.component';
|
import { ActionItemEditComponent } from '../action-item-edit/action-item-edit.component';
|
||||||
import { HermesClientService } from '../../hermes-client.service';
|
import { HermesClientService } from '../../hermes-client.service';
|
||||||
|
import { MatSelectModule } from '@angular/material/select';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'action-list',
|
selector: 'action-list',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [MatButtonModule, MatFormFieldModule, MatIconModule, MatListModule],
|
imports: [
|
||||||
|
MatButtonModule,
|
||||||
|
MatFormFieldModule,
|
||||||
|
MatIconModule,
|
||||||
|
MatListModule,
|
||||||
|
MatSelectModule,
|
||||||
|
],
|
||||||
templateUrl: './action-list.component.html',
|
templateUrl: './action-list.component.html',
|
||||||
styleUrl: './action-list.component.scss'
|
styleUrl: './action-list.component.scss'
|
||||||
})
|
})
|
||||||
export class ActionListComponent {
|
export class ActionListComponent {
|
||||||
@Input() actions: RedeemableAction[] = []
|
@Input() actions: RedeemableAction[] = []
|
||||||
@Output() actionsChange = new EventEmitter<RedeemableAction>();
|
@Output() actionsChange = new EventEmitter<RedeemableAction>();
|
||||||
|
|
||||||
readonly dialog = inject(MatDialog);
|
readonly dialog = inject(MatDialog);
|
||||||
readonly client = inject(HermesClientService);
|
readonly client = inject(HermesClientService);
|
||||||
|
|
||||||
opened = false;
|
opened = false;
|
||||||
|
|
||||||
create(): void {
|
create(): void {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<body>
|
<content>
|
||||||
<h3>Redeemable Actions</h3>
|
<h3>Redeemable Actions</h3>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
@ -31,4 +31,4 @@
|
|||||||
<action-list class="center"
|
<action-list class="center"
|
||||||
[actions]="actions"
|
[actions]="actions"
|
||||||
(actionsChange)="items.push($event)" />
|
(actionsChange)="items.push($event)" />
|
||||||
</body>
|
</content>
|
@ -1,4 +1,4 @@
|
|||||||
import { Component, inject, OnInit } from '@angular/core';
|
import { Component, Inject, inject, OnInit } from '@angular/core';
|
||||||
import { ActionListComponent } from "../action-list/action-list.component";
|
import { ActionListComponent } from "../action-list/action-list.component";
|
||||||
import { MatSelectModule } from '@angular/material/select';
|
import { MatSelectModule } from '@angular/material/select';
|
||||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
@ -9,6 +9,8 @@ import RedeemableAction from '../../shared/models/redeemable-action';
|
|||||||
import { FormControl, ReactiveFormsModule } from '@angular/forms';
|
import { FormControl, ReactiveFormsModule } from '@angular/forms';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
import RedeemableActionService from '../../shared/services/redeemable-action.service';
|
import RedeemableActionService from '../../shared/services/redeemable-action.service';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { DOCUMENT } from '@angular/common';
|
||||||
|
|
||||||
interface IActionFilter {
|
interface IActionFilter {
|
||||||
name: string
|
name: string
|
||||||
@ -17,14 +19,15 @@ interface IActionFilter {
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'actions',
|
selector: 'actions',
|
||||||
standalone: true,
|
|
||||||
imports: [
|
imports: [
|
||||||
ActionListComponent,
|
ActionListComponent,
|
||||||
ReactiveFormsModule,
|
MatButtonModule,
|
||||||
MatFormFieldModule,
|
MatFormFieldModule,
|
||||||
MatIconModule,
|
MatIconModule,
|
||||||
MatInputModule,
|
MatInputModule,
|
||||||
MatSelectModule
|
MatSelectModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
],
|
],
|
||||||
templateUrl: './actions.component.html',
|
templateUrl: './actions.component.html',
|
||||||
styleUrl: './actions.component.scss'
|
styleUrl: './actions.component.scss'
|
||||||
@ -43,11 +46,15 @@ export class ActionsComponent implements OnInit {
|
|||||||
private readonly client = inject(HermesClientService);
|
private readonly client = inject(HermesClientService);
|
||||||
private readonly redeemableActionService = inject(RedeemableActionService);
|
private readonly redeemableActionService = inject(RedeemableActionService);
|
||||||
private readonly route = inject(ActivatedRoute);
|
private readonly route = inject(ActivatedRoute);
|
||||||
|
|
||||||
filter = this.filters[0];
|
filter = this.filters[0];
|
||||||
searchControl = new FormControl('');
|
searchControl = new FormControl('');
|
||||||
search = '';
|
search = '';
|
||||||
items: RedeemableAction[] = [];
|
items: RedeemableAction[] = [];
|
||||||
|
|
||||||
|
constructor(@Inject(DOCUMENT) private document: Document) { }
|
||||||
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.route.data.subscribe(data => {
|
this.route.data.subscribe(data => {
|
||||||
if (!data['redeemableActions'])
|
if (!data['redeemableActions'])
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { isPlatformBrowser } from '@angular/common';
|
import { isPlatformBrowser } from '@angular/common';
|
||||||
import { Component, OnInit, Inject, PLATFORM_ID, NgZone, OnDestroy, inject } from '@angular/core';
|
import { Component, OnInit, Inject, PLATFORM_ID, NgZone, OnDestroy, inject, HostBinding } from '@angular/core';
|
||||||
import { ActivatedRoute, Router, RouterOutlet } from '@angular/router';
|
import { ActivatedRoute, Router, RouterOutlet } from '@angular/router';
|
||||||
import { HermesClientService } from './hermes-client.service';
|
import { HermesClientService } from './hermes-client.service';
|
||||||
import { AuthUserGuard } from './shared/auth/auth.user.guard'
|
import { AuthUserGuard } from './shared/auth/auth.user.guard'
|
||||||
@ -10,23 +10,42 @@ import { ApiAuthenticationService } from './shared/services/api/api-authenticati
|
|||||||
import { AuthModule } from './auth/auth.module';
|
import { AuthModule } from './auth/auth.module';
|
||||||
import { ApiKeyService } from './shared/services/api/api-key.service';
|
import { ApiKeyService } from './shared/services/api/api-key.service';
|
||||||
import ApiKey from './shared/models/api-key';
|
import ApiKey from './shared/models/api-key';
|
||||||
|
import { ThemeService } from './shared/services/theme.service';
|
||||||
|
import { OverlayContainer } from '@angular/cdk/overlay';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [RouterOutlet, AuthModule, NavigationComponent],
|
imports: [
|
||||||
|
RouterOutlet,
|
||||||
|
AuthModule,
|
||||||
|
NavigationComponent
|
||||||
|
],
|
||||||
providers: [AuthUserGuard],
|
providers: [AuthUserGuard],
|
||||||
templateUrl: './app.component.html',
|
templateUrl: './app.component.html',
|
||||||
styleUrl: './app.component.scss'
|
styleUrl: './app.component.scss'
|
||||||
})
|
})
|
||||||
export class AppComponent implements OnInit, OnDestroy {
|
export class AppComponent implements OnInit, OnDestroy {
|
||||||
private readonly keyService = inject(ApiKeyService);
|
private readonly keyService = inject(ApiKeyService);
|
||||||
|
private readonly overlayContainer = inject(OverlayContainer);
|
||||||
|
private readonly themeService = inject(ThemeService);
|
||||||
|
|
||||||
private isBrowser: boolean;
|
private isBrowser: boolean;
|
||||||
private ngZone: NgZone;
|
private ngZone: NgZone;
|
||||||
private subscriptions: Subscription[];
|
private subscriptions: Subscription[];
|
||||||
|
|
||||||
|
|
||||||
|
@HostBinding('class.dark-theme')
|
||||||
|
get isDarkTheme() {
|
||||||
|
return this.themeService.isDarkTheme();
|
||||||
|
}
|
||||||
|
|
||||||
|
@HostBinding('class.light-theme')
|
||||||
|
get isLightTheme() {
|
||||||
|
return this.themeService.isLightTheme();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
constructor(private auth: ApiAuthenticationService, private client: HermesClientService, private events: EventService, private router: Router, ngZone: NgZone, @Inject(PLATFORM_ID) private platformId: Object) {
|
constructor(private auth: ApiAuthenticationService, private client: HermesClientService, private events: EventService, private router: Router, ngZone: NgZone, @Inject(PLATFORM_ID) private platformId: Object) {
|
||||||
this.ngZone = ngZone;
|
this.ngZone = ngZone;
|
||||||
this.isBrowser = isPlatformBrowser(this.platformId);
|
this.isBrowser = isPlatformBrowser(this.platformId);
|
||||||
@ -70,7 +89,7 @@ export class AppComponent implements OnInit, OnDestroy {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
this.addSubscription(this.events.listen('login', () => {
|
this.addSubscription(this.events.listen('login', () => {
|
||||||
this.keyService.fetch(true)
|
this.keyService.fetch()
|
||||||
.pipe(timeout(3000), first())
|
.pipe(timeout(3000), first())
|
||||||
.subscribe(async (d: ApiKey[]) => {
|
.subscribe(async (d: ApiKey[]) => {
|
||||||
if (d.length > 0)
|
if (d.length > 0)
|
||||||
@ -80,6 +99,8 @@ export class AppComponent implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
this.overlayContainer.getContainerElement().classList.add(this.themeService.theme + '-theme');
|
||||||
|
|
||||||
this.ngZone.runOutsideAngular(() => setInterval(() => this.client.heartbeat(), 15000));
|
this.ngZone.runOutsideAngular(() => setInterval(() => this.client.heartbeat(), 15000));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,6 +17,6 @@ export const appConfig: ApplicationConfig = {
|
|||||||
}])
|
}])
|
||||||
),
|
),
|
||||||
provideOAuthClient(),
|
provideOAuthClient(),
|
||||||
provideClientHydration(), provideAnimationsAsync()
|
provideClientHydration(), provideAnimationsAsync(), provideAnimationsAsync()
|
||||||
]
|
]
|
||||||
};
|
};
|
13
src/app/app.module.ts
Normal file
13
src/app/app.module.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { MatDialogModule } from '@angular/material/dialog';
|
||||||
|
import { TtsFiltersModule } from './tts-filters/tts-filters.module';
|
||||||
|
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [],
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class AppModule { }
|
@ -23,6 +23,7 @@ import PermissionResolver from './shared/resolvers/permission-resolver';
|
|||||||
import { ConnectionsComponent } from './connections/connections/connections.component';
|
import { ConnectionsComponent } from './connections/connections/connections.component';
|
||||||
import ConnectionResolver from './shared/resolvers/connection-resolver';
|
import ConnectionResolver from './shared/resolvers/connection-resolver';
|
||||||
import { ConnectionCallbackComponent } from './connections/callback/callback.component';
|
import { ConnectionCallbackComponent } from './connections/callback/callback.component';
|
||||||
|
import { KeysComponent } from './keys/keys/keys.component';
|
||||||
|
|
||||||
export const routes: Routes = [
|
export const routes: Routes = [
|
||||||
{
|
{
|
||||||
@ -80,6 +81,14 @@ export const routes: Routes = [
|
|||||||
permissions: PermissionResolver,
|
permissions: PermissionResolver,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'keys',
|
||||||
|
component: KeysComponent,
|
||||||
|
canActivate: [AuthUserGuard],
|
||||||
|
resolve: {
|
||||||
|
keys: ApiKeyResolver,
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'login',
|
path: 'login',
|
||||||
component: LoginComponent,
|
component: LoginComponent,
|
||||||
|
@ -52,7 +52,7 @@ export class ImpersonationComponent implements OnInit {
|
|||||||
.subscribe(async _ =>
|
.subscribe(async _ =>
|
||||||
await setTimeout(async () =>
|
await setTimeout(async () =>
|
||||||
await this.router.navigate([url.substring(1)]), 500));
|
await this.router.navigate([url.substring(1)]), 500));
|
||||||
this.keyService.fetch(true)
|
this.keyService.fetch()
|
||||||
.pipe(timeout(3000), first())
|
.pipe(timeout(3000), first())
|
||||||
.subscribe(async (d: ApiKey[]) => {
|
.subscribe(async (d: ApiKey[]) => {
|
||||||
if (d.length > 0)
|
if (d.length > 0)
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
.mat-mdc-card-content {
|
.mat-mdc-card-content {
|
||||||
align-self: center;
|
align-self: center;
|
||||||
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
</mat-select>
|
</mat-select>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
<mat-card-actions align="end">
|
<mat-card-actions>
|
||||||
<button mat-raised-button
|
<button mat-raised-button
|
||||||
(click)="login()">Log In</button>
|
(click)="login()">Log In</button>
|
||||||
</mat-card-actions>
|
</mat-card-actions>
|
||||||
|
@ -39,7 +39,7 @@ export class TtsLoginComponent implements OnInit, OnDestroy {
|
|||||||
this.subscriptions.push(this.events.listen('impersonation', _ => {
|
this.subscriptions.push(this.events.listen('impersonation', _ => {
|
||||||
this.selected_api_key = undefined;
|
this.selected_api_key = undefined;
|
||||||
|
|
||||||
this.keyService.fetch(true)
|
this.keyService.fetch()
|
||||||
.pipe(timeout(3000), first())
|
.pipe(timeout(3000), first())
|
||||||
.subscribe(d => this.api_keys = d);
|
.subscribe(d => this.api_keys = d);
|
||||||
}));
|
}));
|
||||||
|
@ -8,8 +8,7 @@
|
|||||||
<mat-card-content>
|
<mat-card-content>
|
||||||
<impersonation />
|
<impersonation />
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
<mat-card-actions class="actions"
|
<mat-card-actions class="actions">
|
||||||
align="end">
|
|
||||||
<div>
|
<div>
|
||||||
@if (isTTSLoggedIn) {
|
@if (isTTSLoggedIn) {
|
||||||
<button mat-raised-button
|
<button mat-raised-button
|
||||||
|
@ -2,6 +2,7 @@ import { Component, inject, OnInit } from '@angular/core';
|
|||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { HermesClientService } from '../../hermes-client.service';
|
import { HermesClientService } from '../../hermes-client.service';
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
import { environment } from '../../../environments/environment';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'connection-callback',
|
selector: 'connection-callback',
|
||||||
@ -34,7 +35,7 @@ export class ConnectionCallbackComponent implements OnInit {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.http.get(`https://beta.tomtospeech.com/api/auth/connections?token=${params['access_token']}&state=${params['state']}&expires_in=${params['expires_in']}`).subscribe(async (d: any) => {
|
this.http.get(`${environment.API_HOST}/auth/connections?token=${params['access_token']}&state=${params['state']}&expires_in=${params['expires_in']}`).subscribe(async (d: any) => {
|
||||||
const data = d.data;
|
const data = d.data;
|
||||||
this.success = true;
|
this.success = true;
|
||||||
|
|
||||||
|
@ -41,14 +41,14 @@
|
|||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
|
|
||||||
<mat-card-actions class="actions"
|
<mat-card-actions class="actions">
|
||||||
align="end">
|
<button mat-button
|
||||||
<button mat-raised-button
|
class="neutral"
|
||||||
class="warning"
|
disabled="{{waitForResponse}}"
|
||||||
(click)="dialogRef.close()">Cancel</button>
|
(click)="dialogRef.close()">Cancel</button>
|
||||||
<button mat-raised-button
|
<button mat-button
|
||||||
class="confirm"
|
class="confirm"
|
||||||
disabled="{{form.invalid || waitForResponse}}"
|
disabled="{{!form.dirty || form.invalid || waitForResponse}}"
|
||||||
(click)="submit()">Add</button>
|
(click)="submit()">Add</button>
|
||||||
</mat-card-actions>
|
</mat-card-actions>
|
||||||
|
|
||||||
|
@ -7,8 +7,6 @@ import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
|||||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
import { MatIconModule } from '@angular/material/icon';
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
import { MatInputModule } from '@angular/material/input';
|
import { MatInputModule } from '@angular/material/input';
|
||||||
import { ActionItemEditComponent } from '../../actions/action-item-edit/action-item-edit.component';
|
|
||||||
import { HermesClientService } from '../../hermes-client.service';
|
|
||||||
import { MatSelectModule } from '@angular/material/select';
|
import { MatSelectModule } from '@angular/material/select';
|
||||||
import { DOCUMENT } from '@angular/common';
|
import { DOCUMENT } from '@angular/common';
|
||||||
|
|
||||||
@ -27,10 +25,10 @@ import { DOCUMENT } from '@angular/common';
|
|||||||
styleUrl: './connection-item-edit.component.scss'
|
styleUrl: './connection-item-edit.component.scss'
|
||||||
})
|
})
|
||||||
export class ConnectionItemEditComponent {
|
export class ConnectionItemEditComponent {
|
||||||
private readonly client = inject(HermesClientService);
|
|
||||||
private readonly http = inject(HttpClient);
|
private readonly http = inject(HttpClient);
|
||||||
|
private readonly data = inject<{ name: string }>(MAT_DIALOG_DATA);
|
||||||
|
readonly dialogRef = inject(MatDialogRef<ConnectionItemEditComponent>);
|
||||||
|
|
||||||
readonly data = inject<{ name: string }>(MAT_DIALOG_DATA);
|
|
||||||
readonly nameControl = new FormControl<string>('', [Validators.required]);
|
readonly nameControl = new FormControl<string>('', [Validators.required]);
|
||||||
readonly clientIdControl = new FormControl<string>('', [Validators.required]);
|
readonly clientIdControl = new FormControl<string>('', [Validators.required]);
|
||||||
readonly typeControl = new FormControl<string>('', [Validators.required]);
|
readonly typeControl = new FormControl<string>('', [Validators.required]);
|
||||||
@ -40,7 +38,6 @@ export class ConnectionItemEditComponent {
|
|||||||
type: this.typeControl,
|
type: this.typeControl,
|
||||||
});
|
});
|
||||||
|
|
||||||
readonly dialogRef = inject(MatDialogRef<ActionItemEditComponent>);
|
|
||||||
|
|
||||||
responseError: string | undefined;
|
responseError: string | undefined;
|
||||||
waitForResponse = false;
|
waitForResponse = false;
|
||||||
|
@ -5,13 +5,13 @@
|
|||||||
<article class="right">
|
<article class="right">
|
||||||
<button mat-button
|
<button mat-button
|
||||||
class="neutral"
|
class="neutral"
|
||||||
(click)="renew(connection())">
|
(click)="renew()">
|
||||||
<mat-icon>refresh</mat-icon>
|
<mat-icon>refresh</mat-icon>
|
||||||
Renew
|
Renew
|
||||||
</button>
|
</button>
|
||||||
<button mat-button
|
<button mat-button
|
||||||
class="danger"
|
class="danger"
|
||||||
(click)="delete(connection())">
|
(click)="delete()">
|
||||||
<mat-icon>delete</mat-icon>
|
<mat-icon>delete</mat-icon>
|
||||||
Delete
|
Delete
|
||||||
</button>
|
</button>
|
||||||
|
@ -5,7 +5,6 @@ import { MatIconModule } from '@angular/material/icon';
|
|||||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
import { ReactiveFormsModule } from '@angular/forms';
|
import { ReactiveFormsModule } from '@angular/forms';
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { Router } from '@angular/router';
|
|
||||||
import { DOCUMENT } from '@angular/common';
|
import { DOCUMENT } from '@angular/common';
|
||||||
import { HermesClientService } from '../../hermes-client.service';
|
import { HermesClientService } from '../../hermes-client.service';
|
||||||
|
|
||||||
@ -21,19 +20,19 @@ import { HermesClientService } from '../../hermes-client.service';
|
|||||||
styleUrl: './connection-item.component.scss'
|
styleUrl: './connection-item.component.scss'
|
||||||
})
|
})
|
||||||
export class ConnectionItemComponent {
|
export class ConnectionItemComponent {
|
||||||
router = inject(Router);
|
private readonly http = inject(HttpClient);
|
||||||
http = inject(HttpClient);
|
private readonly client = inject(HermesClientService);
|
||||||
client = inject(HermesClientService);
|
|
||||||
|
|
||||||
connection = input.required<Connection>();
|
connection = input.required<Connection>();
|
||||||
|
|
||||||
constructor(@Inject(DOCUMENT) private document: Document) { }
|
constructor(@Inject(DOCUMENT) private document: Document) { }
|
||||||
|
|
||||||
delete(conn: Connection) {
|
delete() {
|
||||||
this.client.deleteConnection(conn.name);
|
this.client.deleteConnection(this.connection().name);
|
||||||
}
|
}
|
||||||
|
|
||||||
renew(conn: Connection) {
|
renew() {
|
||||||
|
const conn = this.connection();
|
||||||
this.http.post('/api/auth/connections', {
|
this.http.post('/api/auth/connections', {
|
||||||
name: conn.name,
|
name: conn.name,
|
||||||
type: conn.type,
|
type: conn.type,
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
<h3>Connections</h3>
|
<content>
|
||||||
|
<h3>Connections</h3>
|
||||||
|
|
||||||
<connection-list [connections]="connections"/>
|
<connection-list [connections]="connections" />
|
||||||
|
</content>
|
@ -38,13 +38,15 @@
|
|||||||
}
|
}
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
<mat-card-actions align="end">
|
<mat-card-actions>
|
||||||
<button mat-button
|
<button mat-button
|
||||||
|
class="neutral"
|
||||||
[disabled]="waitForResponse"
|
[disabled]="waitForResponse"
|
||||||
(click)="cancel()">
|
(click)="cancel()">
|
||||||
<mat-icon>cancel</mat-icon>Cancel
|
<mat-icon>cancel</mat-icon>Cancel
|
||||||
</button>
|
</button>
|
||||||
<button mat-button
|
<button mat-button
|
||||||
|
class="confirm"
|
||||||
[disabled]="waitForResponse || formGroup.invalid"
|
[disabled]="waitForResponse || formGroup.invalid"
|
||||||
(click)="add()">
|
(click)="add()">
|
||||||
<mat-icon>add</mat-icon>Add
|
<mat-icon>add</mat-icon>Add
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
article {
|
article {
|
||||||
background-color: #f0f0f0;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
border: grey solid 1px;
|
||||||
border-radius: 15px;
|
border-radius: 15px;
|
||||||
padding: 0.5em 1em;
|
padding: 0.5em 1em;
|
||||||
|
|
||||||
|
38
src/app/keys/key-item-edit/key-item-edit.component.html
Normal file
38
src/app/keys/key-item-edit/key-item-edit.component.html
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<mat-card>
|
||||||
|
<mat-card-header>
|
||||||
|
<mat-card-title-group>
|
||||||
|
<mat-card-title>Add API Key</mat-card-title>
|
||||||
|
<mat-card-subtitle></mat-card-subtitle>
|
||||||
|
</mat-card-title-group>
|
||||||
|
</mat-card-header>
|
||||||
|
|
||||||
|
<mat-card-content>
|
||||||
|
<mat-form-field>
|
||||||
|
<mat-label>Key Label</mat-label>
|
||||||
|
<input matInput
|
||||||
|
[formControl]="labelControl" />
|
||||||
|
@if (labelControl.invalid && (labelControl.dirty || labelControl.touched)) {
|
||||||
|
@if (labelControl.hasError('required')) {
|
||||||
|
<small class="error">This field is required.</small>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</mat-form-field>
|
||||||
|
</mat-card-content>
|
||||||
|
|
||||||
|
<mat-card-actions class="actions">
|
||||||
|
<button mat-button
|
||||||
|
class="neutral"
|
||||||
|
disabled="{{waitForResponse}}"
|
||||||
|
(click)="dialogRef.close()">Cancel</button>
|
||||||
|
<button mat-button
|
||||||
|
class="confirm"
|
||||||
|
disabled="{{!form.dirty || form.invalid || waitForResponse}}"
|
||||||
|
(click)="submit()">Add</button>
|
||||||
|
</mat-card-actions>
|
||||||
|
|
||||||
|
@if (responseError) {
|
||||||
|
<mat-card-footer>
|
||||||
|
<small class="error below">{{responseError}}</small>
|
||||||
|
</mat-card-footer>
|
||||||
|
}
|
||||||
|
</mat-card>
|
23
src/app/keys/key-item-edit/key-item-edit.component.spec.ts
Normal file
23
src/app/keys/key-item-edit/key-item-edit.component.spec.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { KeyItemEditComponent } from './key-item-edit.component';
|
||||||
|
|
||||||
|
describe('KeyItemEditComponent', () => {
|
||||||
|
let component: KeyItemEditComponent;
|
||||||
|
let fixture: ComponentFixture<KeyItemEditComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [KeyItemEditComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(KeyItemEditComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
67
src/app/keys/key-item-edit/key-item-edit.component.ts
Normal file
67
src/app/keys/key-item-edit/key-item-edit.component.ts
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
import { Component, inject } from '@angular/core';
|
||||||
|
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { MatCardModule } from '@angular/material/card';
|
||||||
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
import { MatInputModule } from '@angular/material/input';
|
||||||
|
import { MatSelectModule } from '@angular/material/select';
|
||||||
|
import EventService from '../../shared/services/EventService';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'key-item-edit',
|
||||||
|
imports: [
|
||||||
|
MatButtonModule,
|
||||||
|
MatCardModule,
|
||||||
|
MatIconModule,
|
||||||
|
MatFormFieldModule,
|
||||||
|
MatInputModule,
|
||||||
|
MatSelectModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
],
|
||||||
|
templateUrl: './key-item-edit.component.html',
|
||||||
|
styleUrl: './key-item-edit.component.scss'
|
||||||
|
})
|
||||||
|
export class KeyItemEditComponent {
|
||||||
|
private readonly http = inject(HttpClient);
|
||||||
|
private readonly events = inject(EventService);
|
||||||
|
readonly data = inject<{ name: string }>(MAT_DIALOG_DATA);
|
||||||
|
|
||||||
|
readonly labelControl = new FormControl<string>('', [Validators.required]);
|
||||||
|
readonly form = new FormGroup({
|
||||||
|
name: this.labelControl,
|
||||||
|
});
|
||||||
|
|
||||||
|
readonly dialogRef = inject(MatDialogRef<KeyItemEditComponent>);
|
||||||
|
|
||||||
|
responseError: string | undefined;
|
||||||
|
waitForResponse = false;
|
||||||
|
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.labelControl.setValue(this.data.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
submit(): void {
|
||||||
|
if (this.form.invalid || this.waitForResponse) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const label = this.labelControl.value;
|
||||||
|
this.http.post('/api/keys', { label },
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
'Authorization': 'Bearer ' + localStorage.getItem('jwt')
|
||||||
|
}
|
||||||
|
}).subscribe(async (d: any) => {
|
||||||
|
this.events.emit('add_api_key', {
|
||||||
|
id: d.key,
|
||||||
|
label: d.label,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.dialogRef.close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
20
src/app/keys/key-item/key-item.component.html
Normal file
20
src/app/keys/key-item/key-item.component.html
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<section>
|
||||||
|
{{(isVisible ? key().id : key().label)}}
|
||||||
|
|
||||||
|
<article class="right">
|
||||||
|
<button mat-button
|
||||||
|
class="neutral"
|
||||||
|
[disabled]="waitForResponse"
|
||||||
|
(click)="isVisible = !isVisible">
|
||||||
|
<mat-icon>{{(isVisible ? "visibility_off" : "visibility")}}</mat-icon>
|
||||||
|
{{(isVisible ? "Hide" : "View")}} Key
|
||||||
|
</button>
|
||||||
|
<button mat-button
|
||||||
|
class="danger"
|
||||||
|
[disabled]="waitForResponse"
|
||||||
|
(click)="delete()">
|
||||||
|
<mat-icon>delete</mat-icon>
|
||||||
|
Delete
|
||||||
|
</button>
|
||||||
|
</article>
|
||||||
|
</section>
|
11
src/app/keys/key-item/key-item.component.scss
Normal file
11
src/app/keys/key-item/key-item.component.scss
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
section {
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
margin: 0 0.5em;
|
||||||
|
}
|
23
src/app/keys/key-item/key-item.component.spec.ts
Normal file
23
src/app/keys/key-item/key-item.component.spec.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { KeyItemComponent } from './key-item.component';
|
||||||
|
|
||||||
|
describe('KeyItemComponent', () => {
|
||||||
|
let component: KeyItemComponent;
|
||||||
|
let fixture: ComponentFixture<KeyItemComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [KeyItemComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(KeyItemComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
48
src/app/keys/key-item/key-item.component.ts
Normal file
48
src/app/keys/key-item/key-item.component.ts
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import { Component, inject, input } from '@angular/core';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
|
import { ReactiveFormsModule } from '@angular/forms';
|
||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
import ApiKey from '../../shared/models/api-key';
|
||||||
|
import EventService from '../../shared/services/EventService';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'key-item',
|
||||||
|
imports: [
|
||||||
|
MatButtonModule,
|
||||||
|
MatIconModule,
|
||||||
|
MatFormFieldModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
],
|
||||||
|
templateUrl: './key-item.component.html',
|
||||||
|
styleUrl: './key-item.component.scss'
|
||||||
|
})
|
||||||
|
export class KeyItemComponent {
|
||||||
|
private readonly http = inject(HttpClient);
|
||||||
|
private readonly events = inject(EventService);
|
||||||
|
|
||||||
|
key = input.required<ApiKey>();
|
||||||
|
isVisible: boolean = false;
|
||||||
|
waitForResponse = false;
|
||||||
|
|
||||||
|
delete() {
|
||||||
|
if (this.waitForResponse) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const key_id = this.key().id;
|
||||||
|
this.http.delete('/api/keys',
|
||||||
|
{
|
||||||
|
body: {
|
||||||
|
key: key_id,
|
||||||
|
},
|
||||||
|
headers: {
|
||||||
|
'Authorization': 'Bearer ' + localStorage.getItem('jwt')
|
||||||
|
}
|
||||||
|
}).subscribe(async (d: any) => {
|
||||||
|
this.events.emit('delete_api_key', key_id);
|
||||||
|
this.waitForResponse = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
28
src/app/keys/key-list/key-list.component.html
Normal file
28
src/app/keys/key-list/key-list.component.html
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<ul>
|
||||||
|
<li class="header">
|
||||||
|
<mat-form-field appearance="outline"
|
||||||
|
subscriptSizing="dynamic">
|
||||||
|
<mat-label>Label Filter</mat-label>
|
||||||
|
<input matInput
|
||||||
|
placeholder="Filter keys by label"
|
||||||
|
[formControl]="searchControl" />
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<button mat-icon-button
|
||||||
|
(click)="add()">
|
||||||
|
<mat-icon>add</mat-icon>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
@for (key of keys; track key.id) {
|
||||||
|
<li>
|
||||||
|
<key-item [key]="key" />
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
@if (!keys.length) {
|
||||||
|
@if (searchControl.value) {
|
||||||
|
<p class="notice">No API keys match the filter.</p>
|
||||||
|
} @else {
|
||||||
|
<p class="notice">No API keys available.</p>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</ul>
|
38
src/app/keys/key-list/key-list.component.scss
Normal file
38
src/app/keys/key-list/key-list.component.scss
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
@use '@angular/material' as mat;
|
||||||
|
|
||||||
|
ul {
|
||||||
|
@include mat.all-component-densities(-5);
|
||||||
|
|
||||||
|
@include mat.form-field-overrides((
|
||||||
|
outlined-outline-color: rgb(167, 88, 199),
|
||||||
|
outlined-focus-label-text-color: rgb(155, 57, 194),
|
||||||
|
outlined-focus-outline-color: rgb(155, 57, 194),
|
||||||
|
));
|
||||||
|
|
||||||
|
background-color: rgb(202, 68, 255);
|
||||||
|
border-radius: 15px;
|
||||||
|
margin: 0 0;
|
||||||
|
padding: 0;
|
||||||
|
max-width: 600px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul li {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
background-color: rgb(240, 165, 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
ul li.header {
|
||||||
|
background-color: rgb(215, 115, 255);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-around;
|
||||||
|
flex-direction: row;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul .notice {
|
||||||
|
text-align: center;
|
||||||
|
}
|
23
src/app/keys/key-list/key-list.component.spec.ts
Normal file
23
src/app/keys/key-list/key-list.component.spec.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { KeyListComponent } from './key-list.component';
|
||||||
|
|
||||||
|
describe('KeyListComponent', () => {
|
||||||
|
let component: KeyListComponent;
|
||||||
|
let fixture: ComponentFixture<KeyListComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [KeyListComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(KeyListComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
61
src/app/keys/key-list/key-list.component.ts
Normal file
61
src/app/keys/key-list/key-list.component.ts
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import { Component, inject, Input } from '@angular/core';
|
||||||
|
import { FormControl, ReactiveFormsModule } from '@angular/forms';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { MatCardModule } from '@angular/material/card';
|
||||||
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
import { MatInputModule } from '@angular/material/input';
|
||||||
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
|
import { MatSelectModule } from '@angular/material/select';
|
||||||
|
import { containsLettersInOrder } from '../../shared/utils/string-compare';
|
||||||
|
import { KeyItemComponent } from '../key-item/key-item.component';
|
||||||
|
import { KeyItemEditComponent } from '../key-item-edit/key-item-edit.component';
|
||||||
|
import ApiKey from '../../shared/models/api-key';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'key-list',
|
||||||
|
imports: [
|
||||||
|
MatButtonModule,
|
||||||
|
MatCardModule,
|
||||||
|
MatFormFieldModule,
|
||||||
|
MatIconModule,
|
||||||
|
MatInputModule,
|
||||||
|
MatSelectModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
KeyItemComponent,
|
||||||
|
],
|
||||||
|
templateUrl: './key-list.component.html',
|
||||||
|
styleUrl: './key-list.component.scss'
|
||||||
|
})
|
||||||
|
export class KeyListComponent {
|
||||||
|
private readonly _dialog = inject(MatDialog);
|
||||||
|
|
||||||
|
private _keys: ApiKey[] = [];
|
||||||
|
|
||||||
|
readonly searchControl = new FormControl<string>('');
|
||||||
|
|
||||||
|
opened = false;
|
||||||
|
|
||||||
|
|
||||||
|
get keys() {
|
||||||
|
return this._keys.filter(c => containsLettersInOrder(c.label, this.searchControl.value));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Input({ required: true })
|
||||||
|
set keys(value: ApiKey[]) {
|
||||||
|
this._keys = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
add() {
|
||||||
|
if (this.opened)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.opened = true;
|
||||||
|
|
||||||
|
const dialogRef = this._dialog.open(KeyItemEditComponent, {
|
||||||
|
data: { name: this.searchControl.value },
|
||||||
|
});
|
||||||
|
|
||||||
|
dialogRef.afterClosed().subscribe((_: any) => this.opened = false);
|
||||||
|
}
|
||||||
|
}
|
12
src/app/keys/keys.module.ts
Normal file
12
src/app/keys/keys.module.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [],
|
||||||
|
imports: [
|
||||||
|
CommonModule
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class KeysModule { }
|
2
src/app/keys/keys/keys.component.html
Normal file
2
src/app/keys/keys/keys.component.html
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
<h3>API Keys</h3>
|
||||||
|
<key-list [keys]="keys" />
|
0
src/app/keys/keys/keys.component.scss
Normal file
0
src/app/keys/keys/keys.component.scss
Normal file
23
src/app/keys/keys/keys.component.spec.ts
Normal file
23
src/app/keys/keys/keys.component.spec.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { KeysComponent } from './keys.component';
|
||||||
|
|
||||||
|
describe('KeysComponent', () => {
|
||||||
|
let component: KeysComponent;
|
||||||
|
let fixture: ComponentFixture<KeysComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [KeysComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(KeysComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
39
src/app/keys/keys/keys.component.ts
Normal file
39
src/app/keys/keys/keys.component.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import { Component, inject, OnDestroy } from '@angular/core';
|
||||||
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
import { Subscription } from 'rxjs';
|
||||||
|
import { KeyListComponent } from '../key-list/key-list.component';
|
||||||
|
import ApiKey from '../../shared/models/api-key';
|
||||||
|
import EventService from '../../shared/services/EventService';
|
||||||
|
import { ApiKeyService } from '../../shared/services/api/api-key.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'keys',
|
||||||
|
imports: [KeyListComponent],
|
||||||
|
templateUrl: './keys.component.html',
|
||||||
|
styleUrl: './keys.component.scss'
|
||||||
|
})
|
||||||
|
export class KeysComponent implements OnDestroy {
|
||||||
|
private readonly route = inject(ActivatedRoute);
|
||||||
|
private readonly events = inject(EventService);
|
||||||
|
private readonly keyService = inject(ApiKeyService);
|
||||||
|
|
||||||
|
subscriptions: (Subscription | undefined)[] = [];
|
||||||
|
keys: ApiKey[] = [];
|
||||||
|
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.route.data.subscribe(payload => {
|
||||||
|
this.keys = payload['keys'] ?? [];
|
||||||
|
});
|
||||||
|
|
||||||
|
this.subscriptions.push(this.events.listen('add_api_key', _ => this.keyService.fetch().subscribe(keys => this.keys = keys)));
|
||||||
|
this.subscriptions.push(this.events.listen('delete_api_key', _ => this.keyService.fetch().subscribe(keys => this.keys = keys)));
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
for (let subscription of this.subscriptions) {
|
||||||
|
if (subscription)
|
||||||
|
subscription.unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
<nav>
|
<nav>
|
||||||
<user-card class="card" />
|
<user-card class="card" />
|
||||||
<ul>
|
<ul class="">
|
||||||
@if (!isLoggedIn()) {
|
@if (!isLoggedIn()) {
|
||||||
<li>
|
<li>
|
||||||
<a routerLink="/login"
|
<a routerLink="/login"
|
||||||
@ -11,7 +11,8 @@
|
|||||||
}
|
}
|
||||||
@if (isLoggedIn() && !isTTSLoggedIn()) {
|
@if (isLoggedIn() && !isTTSLoggedIn()) {
|
||||||
<li>
|
<li>
|
||||||
<a routerLink="/tts-login"
|
<a mat-raised-button
|
||||||
|
routerLink="/tts-login"
|
||||||
routerLinkActive="active">
|
routerLinkActive="active">
|
||||||
TTS Login
|
TTS Login
|
||||||
</a>
|
</a>
|
||||||
@ -19,42 +20,56 @@
|
|||||||
}
|
}
|
||||||
@if (isLoggedIn() && isTTSLoggedIn()) {
|
@if (isLoggedIn() && isTTSLoggedIn()) {
|
||||||
<li>
|
<li>
|
||||||
<a routerLink="/policies"
|
<a mat-raised-button
|
||||||
|
routerLink="/policies"
|
||||||
routerLinkActive="active">
|
routerLinkActive="active">
|
||||||
Policies
|
Policies
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a routerLink="/filters"
|
<a mat-raised-button
|
||||||
|
routerLink="/filters"
|
||||||
routerLinkActive="active">
|
routerLinkActive="active">
|
||||||
Filters
|
Filters
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a routerLink="/actions"
|
<a mat-raised-button
|
||||||
|
routerLink="/actions"
|
||||||
routerLinkActive="active">
|
routerLinkActive="active">
|
||||||
Actions
|
Actions
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a routerLink="/redemptions"
|
<a mat-raised-button
|
||||||
|
routerLink="/redemptions"
|
||||||
routerLinkActive="active">
|
routerLinkActive="active">
|
||||||
Redemptions
|
Redemptions
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a routerLink="/groups"
|
<a mat-raised-button
|
||||||
|
routerLink="/groups"
|
||||||
routerLinkActive="active">
|
routerLinkActive="active">
|
||||||
Groups
|
Groups
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a routerLink="/connections"
|
<a mat-raised-button
|
||||||
|
routerLink="/connections"
|
||||||
routerLinkActive="active">
|
routerLinkActive="active">
|
||||||
Connections
|
Connections
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
|
@if (isLoggedIn()) {
|
||||||
|
<li>
|
||||||
|
<a mat-raised-button
|
||||||
|
routerLink="/keys"
|
||||||
|
routerLinkActive="active">
|
||||||
|
API Keys
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
}
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
@ -1,8 +1,4 @@
|
|||||||
$primary_background_color: #EEEEEE;
|
@use "@angular/material" as mat;
|
||||||
$primary_font_color: #111111;
|
|
||||||
|
|
||||||
$secondary_background_color: #DDDDDD;
|
|
||||||
$secondary_font_color: #333333;
|
|
||||||
|
|
||||||
ul {
|
ul {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
@ -18,20 +14,17 @@ li {
|
|||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
background-color: transparent;
|
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
border: 0;
|
border: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: large;
|
font-size: large;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: $primary_font_color;
|
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
}
|
|
||||||
|
|
||||||
a:hover {
|
@include mat.button-overrides(
|
||||||
background-color: #FAFAFA;
|
(
|
||||||
}
|
protected-container-height: 1
|
||||||
|
)
|
||||||
a.active {
|
);
|
||||||
background-color: #F5F5F5;
|
|
||||||
}
|
}
|
@ -5,11 +5,18 @@ import { ApiAuthenticationService } from '../shared/services/api/api-authenticat
|
|||||||
import { MatCardModule } from '@angular/material/card';
|
import { MatCardModule } from '@angular/material/card';
|
||||||
import { AuthModule } from '../auth/auth.module';
|
import { AuthModule } from '../auth/auth.module';
|
||||||
import { UserCardComponent } from "../auth/user-card/user-card.component";
|
import { UserCardComponent } from "../auth/user-card/user-card.component";
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'navigation',
|
selector: 'navigation',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [RouterModule, MatCardModule, AuthModule, UserCardComponent],
|
imports: [
|
||||||
|
AuthModule,
|
||||||
|
MatButtonModule,
|
||||||
|
MatCardModule,
|
||||||
|
RouterModule,
|
||||||
|
UserCardComponent,
|
||||||
|
],
|
||||||
templateUrl: './navigation.component.html',
|
templateUrl: './navigation.component.html',
|
||||||
styleUrl: './navigation.component.scss'
|
styleUrl: './navigation.component.scss'
|
||||||
})
|
})
|
||||||
|
@ -23,12 +23,19 @@
|
|||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
|
|
||||||
<mat-card-actions class="actions">
|
<mat-card-actions class="actions">
|
||||||
<button mat-raised-button
|
<button mat-button
|
||||||
(click)="dialogRef.close()">Cancel</button>
|
class="neutral"
|
||||||
|
disabled="{{waitForResponse}}"
|
||||||
|
(click)="dialogRef.close()">
|
||||||
|
<mat-icon>cancel</mat-icon>Cancel
|
||||||
|
</button>
|
||||||
|
|
||||||
<button mat-raised-button
|
<button mat-button
|
||||||
disabled="{{pathControl.invalid || waitForResponse}}"
|
class="confirm"
|
||||||
(click)="submit()">{{data.isNew ? "Add" : "Save"}}</button>
|
disabled="{{!pathControl.dirty || pathControl.invalid || waitForResponse}}"
|
||||||
|
(click)="submit()">
|
||||||
|
<mat-icon>{{data.isNew ? "add" : "save"}}</mat-icon>{{data.isNew ? "Add" : "Save"}}
|
||||||
|
</button>
|
||||||
</mat-card-actions>
|
</mat-card-actions>
|
||||||
|
|
||||||
@if (responseError) {
|
@if (responseError) {
|
||||||
|
@ -52,22 +52,17 @@
|
|||||||
}
|
}
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
<mat-card-actions align="end">
|
<mat-card-actions>
|
||||||
<button mat-button
|
<button mat-button
|
||||||
|
class="neutral"
|
||||||
(click)="dialogRef.close()">
|
(click)="dialogRef.close()">
|
||||||
<mat-icon>cancel</mat-icon>Cancel
|
<mat-icon>cancel</mat-icon>Cancel
|
||||||
</button>
|
</button>
|
||||||
@if (isNew) {
|
|
||||||
<button mat-button
|
<button mat-button
|
||||||
|
class="confirm"
|
||||||
(click)="save()">
|
(click)="save()">
|
||||||
<mat-icon>add</mat-icon>Add
|
<mat-icon>{{(isNew ? 'add' : 'save')}}</mat-icon>{{(isNew ? 'Add' : 'Save')}}
|
||||||
</button>
|
</button>
|
||||||
} @else {
|
|
||||||
<button mat-button
|
|
||||||
(click)="save()">
|
|
||||||
<mat-icon>save</mat-icon>Save
|
|
||||||
</button>
|
|
||||||
}
|
|
||||||
</mat-card-actions>
|
</mat-card-actions>
|
||||||
|
|
||||||
@if (responseError) {
|
@if (responseError) {
|
||||||
|
@ -2,7 +2,7 @@ import { AfterViewInit, Component, inject, OnInit, ViewChild } from '@angular/co
|
|||||||
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
|
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
|
||||||
import { MatButtonModule } from '@angular/material/button';
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
import { MatCardModule } from '@angular/material/card';
|
import { MatCardModule } from '@angular/material/card';
|
||||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog';
|
||||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
import { MatIconModule } from '@angular/material/icon';
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
import { MatInputModule } from '@angular/material/input';
|
import { MatInputModule } from '@angular/material/input';
|
||||||
@ -18,6 +18,7 @@ import { PolicyDropdownComponent } from '../policy-dropdown/policy-dropdown.comp
|
|||||||
GroupDropdownComponent,
|
GroupDropdownComponent,
|
||||||
MatButtonModule,
|
MatButtonModule,
|
||||||
MatCardModule,
|
MatCardModule,
|
||||||
|
MatDialogModule,
|
||||||
MatFormFieldModule,
|
MatFormFieldModule,
|
||||||
MatIconModule,
|
MatIconModule,
|
||||||
MatInputModule,
|
MatInputModule,
|
||||||
|
@ -31,23 +31,25 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<div class="buttons">
|
|
||||||
<button mat-icon-button
|
|
||||||
class="save"
|
|
||||||
[disabled]="waitForResponse || formGroups.invalid"
|
|
||||||
(click)="save()">
|
|
||||||
<mat-icon>save</mat-icon>
|
|
||||||
</button>
|
|
||||||
@if (redemption.id) {
|
|
||||||
<button mat-icon-button
|
|
||||||
class="delete"
|
|
||||||
[disabled]="waitForResponse"
|
|
||||||
(click)="delete()">
|
|
||||||
<mat-icon>delete</mat-icon>
|
|
||||||
</button>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
|
|
||||||
|
<mat-card-actions>
|
||||||
|
@if (redemption.id) {
|
||||||
|
<button mat-button
|
||||||
|
class="danger"
|
||||||
|
[disabled]="waitForResponse"
|
||||||
|
(click)="delete()">
|
||||||
|
<mat-icon>delete</mat-icon>Delete
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
<button mat-button
|
||||||
|
class="confirm"
|
||||||
|
[disabled]="waitForResponse || formGroups.invalid"
|
||||||
|
(click)="save()">
|
||||||
|
<mat-icon>save</mat-icon>Save
|
||||||
|
</button>
|
||||||
|
</mat-card-actions>
|
||||||
|
|
||||||
@if (responseError) {
|
@if (responseError) {
|
||||||
<mat-card-footer>
|
<mat-card-footer>
|
||||||
<small class="error below">{{responseError}}</small>
|
<small class="error below">{{responseError}}</small>
|
||||||
|
@ -1,46 +1,4 @@
|
|||||||
.mat-mdc-card {
|
|
||||||
margin: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mat-mdc-card-content {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
column-gap: 1em;
|
|
||||||
padding-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.buttons {
|
.buttons {
|
||||||
margin: -0.75em;
|
margin: -0.75em;
|
||||||
}
|
|
||||||
|
|
||||||
button,
|
|
||||||
.mdc-button {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width:1000px) {
|
|
||||||
.mat-mdc-card-content {
|
|
||||||
flex-direction: column;
|
|
||||||
margin-bottom: 0.5em;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
margin-top: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.buttons {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
button~button {
|
|
||||||
margin-left: 2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.delete {
|
|
||||||
background-color: rgb(255, 152, 152);
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -29,8 +29,7 @@
|
|||||||
<th mat-header-cell
|
<th mat-header-cell
|
||||||
*matHeaderCellDef>Twitch Redemption Name</th>
|
*matHeaderCellDef>Twitch Redemption Name</th>
|
||||||
<td mat-cell
|
<td mat-cell
|
||||||
*matCellDef="let redemption">{{getTwitchRedemptionNameById(redemption.redemption_id) || 'Unknown
|
*matCellDef="let redemption">{{getTwitchRedemptionNameById(redemption.redemption_id) || 'Unknown Twitch Redemption'}}</td>
|
||||||
Twitch Redemption'}}</td>
|
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="action-name">
|
<ng-container matColumnDef="action-name">
|
||||||
|
@ -20,10 +20,13 @@ export class ApiKeyService {
|
|||||||
this.keys = [];
|
this.keys = [];
|
||||||
this.loaded = false;
|
this.loaded = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.events.listen('delete_api_key', payload => this.keys = this.keys.filter(k => k.id != payload));
|
||||||
|
this.events.listen('add_api_key', payload => this.keys.push(payload));
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch(force: boolean = false) {
|
fetch() {
|
||||||
if (!force && this.loaded)
|
if (this.loaded)
|
||||||
return of(this.keys);
|
return of(this.keys);
|
||||||
|
|
||||||
const $ = this.http.get<ApiKey[]>(environment.API_HOST + '/keys', {
|
const $ = this.http.get<ApiKey[]>(environment.API_HOST + '/keys', {
|
||||||
|
16
src/app/shared/services/theme.service.spec.ts
Normal file
16
src/app/shared/services/theme.service.spec.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ThemeService } from './theme.service';
|
||||||
|
|
||||||
|
describe('ThemeService', () => {
|
||||||
|
let service: ThemeService;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({});
|
||||||
|
service = TestBed.inject(ThemeService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
32
src/app/shared/services/theme.service.ts
Normal file
32
src/app/shared/services/theme.service.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import { Injectable, signal } from '@angular/core';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class ThemeService {
|
||||||
|
private _current = signal<'light' | 'dark'>('dark');
|
||||||
|
|
||||||
|
get theme() {
|
||||||
|
return this._current();
|
||||||
|
}
|
||||||
|
|
||||||
|
set theme(value: 'light' | 'dark') {
|
||||||
|
this._current.set(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
isDarkTheme() {
|
||||||
|
return this.theme == 'dark';
|
||||||
|
}
|
||||||
|
|
||||||
|
isLightTheme() {
|
||||||
|
return this.theme == 'light';
|
||||||
|
}
|
||||||
|
|
||||||
|
toggle() {
|
||||||
|
if (this.theme == 'light') {
|
||||||
|
this.theme = 'dark';
|
||||||
|
} else {
|
||||||
|
this.theme = 'light';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -22,8 +22,8 @@ export default class TwitchRedemptionService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch(force: boolean = false) {
|
fetch() {
|
||||||
if (!force && this.loaded)
|
if (this.loaded)
|
||||||
return of(this.twitchRedemptions);
|
return of(this.twitchRedemptions);
|
||||||
|
|
||||||
const $ = this.http.get<TwitchRedemption[]>(environment.API_HOST + '/twitch/redemptions', {
|
const $ = this.http.get<TwitchRedemption[]>(environment.API_HOST + '/twitch/redemptions', {
|
||||||
|
6
src/app/theme/theme.component.html
Normal file
6
src/app/theme/theme.component.html
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<button mat-icon-button
|
||||||
|
class="refresh"
|
||||||
|
aria-label="Change theme"
|
||||||
|
(click)="toggle()">
|
||||||
|
<mat-icon>refresh</mat-icon>
|
||||||
|
</button>
|
0
src/app/theme/theme.component.scss
Normal file
0
src/app/theme/theme.component.scss
Normal file
23
src/app/theme/theme.component.spec.ts
Normal file
23
src/app/theme/theme.component.spec.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ThemeComponent } from './theme.component';
|
||||||
|
|
||||||
|
describe('ThemeComponent', () => {
|
||||||
|
let component: ThemeComponent;
|
||||||
|
let fixture: ComponentFixture<ThemeComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [ThemeComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(ThemeComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
35
src/app/theme/theme.component.ts
Normal file
35
src/app/theme/theme.component.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { Component, HostBinding, inject } from '@angular/core';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
import { ThemeService } from '../shared/services/theme.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'theme',
|
||||||
|
imports: [
|
||||||
|
MatButtonModule,
|
||||||
|
MatIconModule,
|
||||||
|
],
|
||||||
|
templateUrl: './theme.component.html',
|
||||||
|
styleUrl: './theme.component.scss'
|
||||||
|
})
|
||||||
|
export class ThemeComponent {
|
||||||
|
private readonly _themeService = inject(ThemeService);
|
||||||
|
|
||||||
|
@HostBinding('class.dark-theme')
|
||||||
|
get isDarkMode() {
|
||||||
|
return this._themeService.isDarkTheme();
|
||||||
|
}
|
||||||
|
|
||||||
|
@HostBinding('class.light-theme')
|
||||||
|
get isLightMode() {
|
||||||
|
return this._themeService.isLightTheme();
|
||||||
|
}
|
||||||
|
|
||||||
|
set(theme: 'light' | 'dark') {
|
||||||
|
this._themeService.theme = theme;
|
||||||
|
}
|
||||||
|
|
||||||
|
toggle() {
|
||||||
|
this._themeService.toggle();
|
||||||
|
}
|
||||||
|
}
|
@ -1,59 +1,66 @@
|
|||||||
<mat-card>
|
<mat-card>
|
||||||
|
<mat-card-header>
|
||||||
|
<mat-card-title>TTS Filter</mat-card-title>
|
||||||
|
</mat-card-header>
|
||||||
|
|
||||||
<mat-card-content>
|
<mat-card-content>
|
||||||
<h3 mat-dialog-title>TTS Filter</h3>
|
<form [formGroup]="forms">
|
||||||
<mat-dialog-content>
|
<div>
|
||||||
<form [formGroup]="forms">
|
<mat-form-field>
|
||||||
<div>
|
<mat-label>Search</mat-label>
|
||||||
<mat-form-field>
|
<input matInput
|
||||||
<mat-label>Search</mat-label>
|
cdkFocusInitial
|
||||||
<input matInput
|
type="text"
|
||||||
cdkFocusInitial
|
formControlName="search" />
|
||||||
type="text"
|
@if (forms.get('search')?.invalid && (forms.get('search')?.dirty || forms.get('search')?.touched)) {
|
||||||
formControlName="search" />
|
<small class="error">Search is required.</small>
|
||||||
@if (forms.get('search')?.invalid && (forms.get('search')?.dirty || forms.get('search')?.touched)) {
|
}
|
||||||
<small class="error">Search is required.</small>
|
</mat-form-field>
|
||||||
}
|
</div>
|
||||||
</mat-form-field>
|
<div>
|
||||||
</div>
|
<mat-form-field>
|
||||||
<div>
|
<mat-label>Replace</mat-label>
|
||||||
<mat-form-field>
|
<input matInput
|
||||||
<mat-label>Replace</mat-label>
|
formControlName="replace" />
|
||||||
<input matInput
|
</mat-form-field>
|
||||||
formControlName="replace" />
|
</div>
|
||||||
</mat-form-field>
|
<div>
|
||||||
</div>
|
<mat-form-field>
|
||||||
<div>
|
<mat-label>Regex Options</mat-label>
|
||||||
<mat-form-field>
|
<mat-select multiple
|
||||||
<mat-label>Regex Options</mat-label>
|
[formControl]="flagControl"
|
||||||
<mat-select multiple
|
[compareWith]="compare"
|
||||||
[formControl]="flagControl"
|
(selectionChange)="onSelectionChange($event)">
|
||||||
[compareWith]="compare"
|
<mat-select-trigger>
|
||||||
(selectionChange)="onSelectionChange($event)">
|
{{optionsSelected[0] || ''}}
|
||||||
<mat-select-trigger>
|
@if ((flagControl.value?.length || 0) > 1) {
|
||||||
{{optionsSelected[0] || ''}}
|
<small class="additional-selection">
|
||||||
@if ((flagControl.value?.length || 0) > 1) {
|
(+{{flagControl.value!.length - 1}})
|
||||||
<small class="additional-selection">
|
</small>
|
||||||
(+{{flagControl.value!.length - 1}})
|
|
||||||
</small>
|
|
||||||
}
|
|
||||||
</mat-select-trigger>
|
|
||||||
@for (option of regexOptions; track option) {
|
|
||||||
<mat-option [value]="option">{{option.name}}</mat-option>
|
|
||||||
}
|
}
|
||||||
</mat-select>
|
</mat-select-trigger>
|
||||||
</mat-form-field>
|
@for (option of regexOptions; track option) {
|
||||||
</div>
|
<mat-option [value]="option">{{option.name}}</mat-option>
|
||||||
@if (responseError) {
|
}
|
||||||
<small class="error">{{responseError}}</small>
|
</mat-select>
|
||||||
}
|
</mat-form-field>
|
||||||
</form>
|
</div>
|
||||||
</mat-dialog-content>
|
@if (responseError) {
|
||||||
<mat-dialog-actions>
|
<small class="error">{{responseError}}</small>
|
||||||
<button mat-button
|
}
|
||||||
(click)="onCancelClick()">Cancel</button>
|
</form>
|
||||||
<button mat-button
|
|
||||||
(click)="onSaveClick()"
|
|
||||||
[disabled]="!forms.dirty || forms.invalid || waitForResponse">Save</button>
|
|
||||||
</mat-dialog-actions>
|
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
|
|
||||||
|
<mat-card-actions>
|
||||||
|
<button mat-button
|
||||||
|
class="neutral"
|
||||||
|
(click)="onCancelClick()"
|
||||||
|
[disabled]="waitForResponse">
|
||||||
|
<mat-icon>cancel</mat-icon> Cancel</button>
|
||||||
|
<button mat-button
|
||||||
|
class="confirm"
|
||||||
|
(click)="onSaveClick()"
|
||||||
|
[disabled]="!forms.dirty || forms.invalid || waitForResponse">
|
||||||
|
<mat-icon>save</mat-icon>Save</button>
|
||||||
|
</mat-card-actions>
|
||||||
</mat-card>
|
</mat-card>
|
@ -1,5 +1,5 @@
|
|||||||
import { Component, inject } from '@angular/core';
|
import { Component, inject } from '@angular/core';
|
||||||
import { MatDialogRef, MAT_DIALOG_DATA, MatDialogActions, MatDialogTitle, MatDialogContent } from '@angular/material/dialog';
|
import { MatDialogRef, MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
|
||||||
import { Filter } from '../../shared/models/filter';
|
import { Filter } from '../../shared/models/filter';
|
||||||
import { FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';
|
import { FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';
|
||||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
@ -8,6 +8,7 @@ import { MatButtonModule } from '@angular/material/button';
|
|||||||
import { MatCardModule } from '@angular/material/card';
|
import { MatCardModule } from '@angular/material/card';
|
||||||
import { MatSelectChange, MatSelectModule } from '@angular/material/select';
|
import { MatSelectChange, MatSelectModule } from '@angular/material/select';
|
||||||
import { HermesClientService } from '../../hermes-client.service';
|
import { HermesClientService } from '../../hermes-client.service';
|
||||||
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'tts-filter-item-edit',
|
selector: 'tts-filter-item-edit',
|
||||||
@ -16,10 +17,9 @@ import { HermesClientService } from '../../hermes-client.service';
|
|||||||
FormsModule,
|
FormsModule,
|
||||||
MatButtonModule,
|
MatButtonModule,
|
||||||
MatCardModule,
|
MatCardModule,
|
||||||
MatDialogActions,
|
MatDialogModule,
|
||||||
MatDialogContent,
|
|
||||||
MatDialogTitle,
|
|
||||||
MatFormFieldModule,
|
MatFormFieldModule,
|
||||||
|
MatIconModule,
|
||||||
MatInputModule,
|
MatInputModule,
|
||||||
MatSelectModule,
|
MatSelectModule,
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
|
@ -22,7 +22,6 @@ ul {
|
|||||||
}
|
}
|
||||||
|
|
||||||
>button {
|
>button {
|
||||||
background: #dddddd;
|
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
|
|
||||||
:hover {
|
:hover {
|
||||||
|
@ -1,7 +1,3 @@
|
|||||||
div {
|
|
||||||
background-color: #fafafa;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.data {
|
ul.data {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
@ -22,10 +18,6 @@ ul.data {
|
|||||||
>li:last-child {
|
>li:last-child {
|
||||||
border-bottom: 0 solid #aaaaaa;
|
border-bottom: 0 solid #aaaaaa;
|
||||||
}
|
}
|
||||||
|
|
||||||
>li:nth-child(2n) {
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ul.header {
|
ul.header {
|
||||||
@ -33,7 +25,6 @@ ul.header {
|
|||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
justify-content: space-evenly;
|
justify-content: space-evenly;
|
||||||
background-color: #fafafa;
|
|
||||||
|
|
||||||
li {
|
li {
|
||||||
border-bottom: 0 solid #aaaaaa;
|
border-bottom: 0 solid #aaaaaa;
|
||||||
@ -41,7 +32,6 @@ ul.header {
|
|||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
white-space: pre;
|
white-space: pre;
|
||||||
text-align: justify;
|
text-align: justify;
|
||||||
background-color: #fafafa;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
li:nth-child(1),
|
li:nth-child(1),
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
main {
|
main {
|
||||||
background-color: #fafafa;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
|
@ -1,12 +1,17 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { FiltersComponent } from './filters/filters.component';
|
import { FiltersComponent } from './filters/filters.component';
|
||||||
import { FilterListComponent } from './filter-list/filter-list.component';
|
import { FilterListComponent } from './filter-list/filter-list.component';
|
||||||
|
import { FilterItemEditComponent } from './filter-item-edit/filter-item-edit.component';
|
||||||
|
import { FilterItemComponent } from './filter-item/filter-item.component';
|
||||||
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [],
|
declarations: [],
|
||||||
imports: [
|
imports: [
|
||||||
FiltersComponent, FilterListComponent
|
FiltersComponent,
|
||||||
|
FilterItemComponent,
|
||||||
|
FilterItemEditComponent,
|
||||||
|
FilterListComponent,
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class TtsFiltersModule { }
|
export class TtsFiltersModule { }
|
||||||
|
@ -15,11 +15,18 @@
|
|||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
|
|
||||||
<mat-card-actions class="actions">
|
<mat-card-actions class="actions">
|
||||||
<button mat-raised-button
|
<button mat-button
|
||||||
(click)="dialogRef.close()">Cancel</button>
|
class="neutral"
|
||||||
<button mat-raised-button
|
disabled="{{waitForResponse}}"
|
||||||
disabled="{{usernameControl.invalid || waitForResponse}}"
|
(click)="dialogRef.close()">
|
||||||
(click)="submit()">Add</button>
|
<mat-icon>cancel</mat-icon>Cancel
|
||||||
|
</button>
|
||||||
|
<button mat-button
|
||||||
|
class="confirm"
|
||||||
|
disabled="{{!usernameControl.dirty || usernameControl.invalid || waitForResponse}}"
|
||||||
|
(click)="submit()">
|
||||||
|
<mat-icon>add</mat-icon>Add
|
||||||
|
</button>
|
||||||
</mat-card-actions>
|
</mat-card-actions>
|
||||||
|
|
||||||
@if (responseError) {
|
@if (responseError) {
|
||||||
|
@ -14,7 +14,9 @@
|
|||||||
rel="stylesheet">
|
rel="stylesheet">
|
||||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons"
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons"
|
||||||
rel="stylesheet">
|
rel="stylesheet">
|
||||||
</head>
|
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet">
|
||||||
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||||
|
</head>
|
||||||
|
|
||||||
<body class="mat-typography">
|
<body class="mat-typography">
|
||||||
<app-root></app-root>
|
<app-root></app-root>
|
||||||
|
@ -1,21 +1,71 @@
|
|||||||
/* You can add global styles to this file, and also import other style files */
|
@use "@angular/material" as mat;
|
||||||
|
@include mat.core();
|
||||||
|
|
||||||
@use '@angular/material' as mat;
|
html {
|
||||||
|
@include mat.theme((theme-type: light dark,
|
||||||
|
color: (primary: mat.$violet-palette,
|
||||||
|
tertiary: mat.$orange-palette,
|
||||||
|
use-system-variables: true,
|
||||||
|
),
|
||||||
|
typography: Roboto,
|
||||||
|
density: 0,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
html,
|
html,
|
||||||
body {
|
body,
|
||||||
|
main {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body,
|
||||||
|
main,
|
||||||
|
content,
|
||||||
|
nav {
|
||||||
|
background-color: var(--mat-sys-background);
|
||||||
|
color: var(--mat-sys-on-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-mdc-card-header,
|
||||||
|
.mat-mdc-card-actions {
|
||||||
|
color: var(--mat-sys-on-background);
|
||||||
|
margin: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-mdc-card-actions {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-family: Roboto, "Helvetica Neue", sans-serif;
|
font-family: Roboto, "Helvetica Neue", sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.light-theme {
|
||||||
|
color-scheme: light;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark-theme {
|
||||||
|
color-scheme: dark;
|
||||||
|
}
|
||||||
|
|
||||||
.mat-mdc-dialog-surface {
|
.mat-mdc-dialog-surface {
|
||||||
background-color: transparent !important;
|
background-color: transparent !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mat-mdc-card-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-mdc-button {
|
||||||
|
color: var(--mat-sys-on-background);
|
||||||
|
}
|
||||||
|
|
||||||
/* width */
|
/* width */
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
width: 5px;
|
width: 5px;
|
||||||
@ -47,28 +97,28 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.confirm {
|
.confirm {
|
||||||
@include mat.button-overrides((text-state-layer-color: rgb(52, 255, 62),
|
@include mat.button-overrides(( //text-state-layer-color: rgb(52, 255, 62),
|
||||||
text-label-text-color: rgb(71, 218, 78),
|
text-label-text-color: rgb(71, 218, 78),
|
||||||
text-disabled-label-text-color: rgb(71, 218, 78),
|
text-disabled-label-text-color: rgb(71, 218, 78),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
.neutral {
|
.neutral {
|
||||||
@include mat.button-overrides((text-state-layer-color: rgb(64, 141, 255),
|
@include mat.button-overrides(( //text-state-layer-color: rgb(64, 141, 255),
|
||||||
text-label-text-color: rgb(52, 106, 255),
|
text-label-text-color: rgb(52, 106, 255),
|
||||||
text-disabled-label-text-color: rgb(52, 106, 255),
|
text-disabled-label-text-color: rgb(52, 106, 255),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
.warning {
|
.warning {
|
||||||
@include mat.button-overrides((text-state-layer-color: rgb(255, 172, 63),
|
@include mat.button-overrides(( //text-state-layer-color: rgb(255, 172, 63),
|
||||||
text-label-text-color: rgb(255, 145, 19),
|
text-label-text-color: rgb(255, 145, 19),
|
||||||
text-disabled-label-text-color: rgb(255, 145, 19),
|
text-disabled-label-text-color: rgb(255, 145, 19),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
.danger {
|
.danger {
|
||||||
@include mat.button-overrides((text-state-layer-color: rgb(255, 48, 48),
|
@include mat.button-overrides(( //text-state-layer-color: rgb(255, 48, 48),
|
||||||
text-label-text-color: rgb(255, 52, 52),
|
text-label-text-color: rgb(255, 52, 52),
|
||||||
text-disabled-label-text-color: rgb(255, 52, 52),
|
text-disabled-label-text-color: rgb(255, 52, 52),
|
||||||
filled-label-text-color: rgb(255, 52, 52),
|
filled-label-text-color: rgb(255, 52, 52),
|
||||||
@ -90,4 +140,19 @@ body {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
align-self: center;
|
align-self: center;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include mat.button-overrides((text-label-text-color: --mat-sys-on-primary,
|
||||||
|
protected-label-text-color: --mat-sys-on-primary,
|
||||||
|
//state-layer-color: --mat-sys-on-primary,
|
||||||
|
));
|
||||||
|
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-family: Roboto, "Helvetica Neue", sans-serif;
|
||||||
}
|
}
|
Reference in New Issue
Block a user