Auto-formatted every file to keep everything consistent.

This commit is contained in:
Tom
2025-03-18 14:03:07 +00:00
parent 74b282ccfd
commit 9201f9b6c5
91 changed files with 14891 additions and 14767 deletions

View File

@ -1,7 +1,6 @@
<mat-form-field> <mat-form-field>
<mat-label>Redeemable Action</mat-label> <mat-label>Redeemable Action</mat-label>
<input <input matInput
matInput
type="text" type="text"
placeholder="Pick a Redeemable Action" placeholder="Pick a Redeemable Action"
aria-label="redeemable action" aria-label="redeemable action"
@ -9,7 +8,9 @@
[matAutocomplete]="auto" [matAutocomplete]="auto"
(blur)="blur()" (blur)="blur()"
(input)="input()"> (input)="input()">
<mat-autocomplete #auto="matAutocomplete" [displayWith]="displayFn" (optionSelected)="select($event.option.value)"> <mat-autocomplete #auto="matAutocomplete"
[displayWith]="displayFn"
(optionSelected)="select($event.option.value)">
@for (action of filteredActions; track action.name) { @for (action of filteredActions; track action.name) {
<mat-option [value]="action">{{action.name}}</mat-option> <mat-option [value]="action">{{action.name}}</mat-option>
} }

View File

@ -8,11 +8,14 @@
</mat-card-header> </mat-card-header>
<mat-card-content> <mat-card-content>
<form class="grid" [formGroup]="formGroup"> <form class="grid"
[formGroup]="formGroup">
<div class="item"> <div class="item">
<mat-form-field> <mat-form-field>
<mat-label>Redeemable Action Name</mat-label> <mat-label>Redeemable Action Name</mat-label>
<input matInput type="text" formControlName="name"> <input matInput
type="text"
formControlName="name">
@if (isNew && formGroup.get('name')?.invalid && (formGroup.get('name')?.dirty || @if (isNew && formGroup.get('name')?.invalid && (formGroup.get('name')?.dirty ||
formGroup.get('name')?.touched)) { formGroup.get('name')?.touched)) {
@if (formGroup.get('name')?.hasError('required')) { @if (formGroup.get('name')?.hasError('required')) {
@ -27,7 +30,9 @@
<div class="item"> <div class="item">
<mat-form-field> <mat-form-field>
<mat-label>Type</mat-label> <mat-label>Type</mat-label>
<mat-select matInput formControlName="type" (selectionChange)="action.type = $event.value"> <mat-select matInput
formControlName="type"
(selectionChange)="action.type = $event.value">
@for (type of actionTypes; track $index) { @for (type of actionTypes; track $index) {
<mat-option value="{{type}}">{{type}}</mat-option> <mat-option value="{{type}}">{{type}}</mat-option>
} }
@ -50,7 +55,10 @@
@if (field.type == 'text') { @if (field.type == 'text') {
<mat-form-field> <mat-form-field>
<mat-label>{{field.label}}</mat-label> <mat-label>{{field.label}}</mat-label>
<input matInput type="text" placeholder="{{field.placeholder}}" [formControl]="field.control" <input matInput
type="text"
placeholder="{{field.placeholder}}"
[formControl]="field.control"
[(ngModel)]="action.data[field.key]"> [(ngModel)]="action.data[field.key]">
@if (field.control.invalid && (field.control.dirty || field.control.touched)) { @if (field.control.invalid && (field.control.dirty || field.control.touched)) {
@if (field.control.hasError('required')) { @if (field.control.hasError('required')) {
@ -65,7 +73,9 @@
@else if (field.type == 'number') { @else if (field.type == 'number') {
<mat-form-field> <mat-form-field>
<mat-label>{{field.label}}</mat-label> <mat-label>{{field.label}}</mat-label>
<input matInput type="number" [formControl]="field.control"> <input matInput
type="number"
[formControl]="field.control">
@if (field.control.invalid && (field.control.dirty || field.control.touched)) { @if (field.control.invalid && (field.control.dirty || field.control.touched)) {
@if (field.control.hasError('required')) { @if (field.control.hasError('required')) {
<small class="error">This field is required.</small> <small class="error">This field is required.</small>
@ -98,15 +108,16 @@
} }
</mat-card-content> </mat-card-content>
<mat-card-actions class="actions" align="end"> <mat-card-actions class="actions"
align="end">
@if (!isNew) { @if (!isNew) {
<button mat-raised-button class="delete" (click)="deleteAction(action)">Delete</button> <button mat-raised-button
class="delete"
(click)="deleteAction(action)">Delete</button>
} }
<button <button mat-raised-button
mat-raised-button
(click)="dialogRef.close()">Cancel</button> (click)="dialogRef.close()">Cancel</button>
<button <button mat-raised-button
mat-raised-button
disabled="{{!formsDirty || !formsValidity || waitForResponse}}" disabled="{{!formsDirty || !formsValidity || waitForResponse}}"
(click)="save()">Save</button> (click)="save()">Save</button>
</mat-card-actions> </mat-card-actions>

View File

@ -1,11 +1,15 @@
<main> <main>
@for (action of actions; track $index) { @for (action of actions; track $index) {
<button type="button" class="container" (click)="modify(action)"> <button type="button"
class="container"
(click)="modify(action)">
<span class="title">{{action.name}}</span> <span class="title">{{action.name}}</span>
<span class="subtitle">{{action.type}}</span> <span class="subtitle">{{action.type}}</span>
</button> </button>
} }
<button type="button" class="container" (click)="create()"> <button type="button"
class="container"
(click)="create()">
<mat-icon>add</mat-icon> <mat-icon>add</mat-icon>
</button> </button>
</main> </main>

View File

@ -5,7 +5,8 @@
<article> <article>
<mat-form-field> <mat-form-field>
<mat-label>Filter by type</mat-label> <mat-label>Filter by type</mat-label>
<mat-select (selectionChange)="onFilterChange($event.value)" value="0"> <mat-select (selectionChange)="onFilterChange($event.value)"
value="0">
<mat-select-trigger> <mat-select-trigger>
<mat-icon matPrefix>filter_list</mat-icon>&nbsp;{{filter.name}} <mat-icon matPrefix>filter_list</mat-icon>&nbsp;{{filter.name}}
</mat-select-trigger> </mat-select-trigger>
@ -27,5 +28,7 @@
</mat-form-field> </mat-form-field>
</article> </article>
</section> </section>
<action-list class="center" [actions]="actions" (actionsChange)="items.push($event)" /> <action-list class="center"
[actions]="actions"
(actionsChange)="items.push($event)" />
</body> </body>

View File

@ -1,4 +1,5 @@
body, h3 { body,
h3 {
padding: 0; padding: 0;
margin: 0; margin: 0;
} }

View File

@ -2,7 +2,8 @@
<main> <main>
<mat-form-field> <mat-form-field>
<mat-label>User to impersonate</mat-label> <mat-label>User to impersonate</mat-label>
<mat-select (selectionChange)="onChange($event)" [(value)]="impersonated"> <mat-select (selectionChange)="onChange($event)"
[(value)]="impersonated">
<mat-option>{{getUsername()}}</mat-option> <mat-option>{{getUsername()}}</mat-option>
@for (user of users; track user.id) { @for (user of users; track user.id) {
<mat-option [value]="user.id">{{ user.name }}</mat-option> <mat-option [value]="user.id">{{ user.name }}</mat-option>

View File

@ -1,5 +1,6 @@
<div class="login"> <div class="login">
<mat-card class="outer" appearance="outlined"> <mat-card class="outer"
appearance="outlined">
<mat-card-header> <mat-card-header>
<h1 class="title">Login</h1> <h1 class="title">Login</h1>
</mat-card-header> </mat-card-header>
@ -8,7 +9,9 @@
<p>Log in with your favorite livestream service</p> <p>Log in with your favorite livestream service</p>
<a> <a>
<mat-card appearance="outlined" class="twitch" (click)="login()"> <mat-card appearance="outlined"
class="twitch"
(click)="login()">
<mat-card-content> <mat-card-content>
Twitch Twitch
</mat-card-content> </mat-card-content>

View File

@ -17,7 +17,8 @@
</mat-form-field> </mat-form-field>
</mat-card-content> </mat-card-content>
<mat-card-actions align="end"> <mat-card-actions align="end">
<button mat-raised-button (click)="login()">Log In</button> <button mat-raised-button
(click)="login()">Log In</button>
</mat-card-actions> </mat-card-actions>
</mat-card> </mat-card>
</main> </main>

View File

@ -1,6 +1,7 @@
@if (auth.isAuthenticated()) { @if (auth.isAuthenticated()) {
<main> <main>
<mat-card appearance="outlined" class="card"> <mat-card appearance="outlined"
class="card">
<mat-card-header> <mat-card-header>
<mat-card-title>{{username}}</mat-card-title> <mat-card-title>{{username}}</mat-card-title>
</mat-card-header> </mat-card-header>
@ -10,9 +11,11 @@
<mat-card-actions class="actions"> <mat-card-actions class="actions">
<div> <div>
@if (isTTSLoggedIn) { @if (isTTSLoggedIn) {
<button mat-raised-button (click)="client.disconnect()"><span class="disconnect">Disconnect</span></button> <button mat-raised-button
(click)="client.disconnect()"><span class="disconnect">Disconnect</span></button>
} }
<button mat-raised-button (click)="auth.logout()"><span class="logoff">Log Off</span></button> <button mat-raised-button
(click)="auth.logout()"><span class="logoff">Log Off</span></button>
</div> </div>
</mat-card-actions> </mat-card-actions>
</mat-card> </mat-card>

View File

@ -14,7 +14,8 @@ main {
justify-content: center; justify-content: center;
} }
.disconnect, .logoff { .disconnect,
.logoff {
color: red; color: red;
} }

View File

@ -1,7 +1,6 @@
<mat-form-field> <mat-form-field>
<mat-label>Group</mat-label> <mat-label>Group</mat-label>
<input <input matInput
matInput
type="text" type="text"
placeholder="Pick a group" placeholder="Pick a group"
aria-label="group" aria-label="group"
@ -11,7 +10,9 @@
[readonly]="!!groupDisabled" [readonly]="!!groupDisabled"
(blur)="blur()" (blur)="blur()"
(input)="input()"> (input)="input()">
<mat-autocomplete #auto="matAutocomplete" [displayWith]="displayFn" (optionSelected)="select($event.option.value)"> <mat-autocomplete #auto="matAutocomplete"
[displayWith]="displayFn"
(optionSelected)="select($event.option.value)">
@for (group of filteredGroups; track group.id) { @for (group of filteredGroups; track group.id) {
<mat-option [value]="group">{{group.name}}</mat-option> <mat-option [value]="group">{{group.name}}</mat-option>
} }

View File

@ -7,7 +7,10 @@
<mat-card-content> <mat-card-content>
<mat-form-field> <mat-form-field>
<mat-label>Group Name</mat-label> <mat-label>Group Name</mat-label>
<input matInput type="text" [formControl]="nameForm" [disabled]="isSpecial" /> <input matInput
type="text"
[formControl]="nameForm"
[disabled]="isSpecial" />
@if (nameForm.invalid && (nameForm.dirty || nameForm.touched)) { @if (nameForm.invalid && (nameForm.dirty || nameForm.touched)) {
@if (nameForm.hasError('required')) { @if (nameForm.hasError('required')) {
<small class="error">This field is required.</small> <small class="error">This field is required.</small>
@ -16,7 +19,9 @@
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<mat-label>TTS Priority</mat-label> <mat-label>TTS Priority</mat-label>
<input matInput type="number" [formControl]="priorityForm" /> <input matInput
type="number"
[formControl]="priorityForm" />
@if (priorityForm.invalid && (priorityForm.dirty || priorityForm.touched)) { @if (priorityForm.invalid && (priorityForm.dirty || priorityForm.touched)) {
@if (priorityForm.hasError('required')) { @if (priorityForm.hasError('required')) {
<small class="error">This field is required.</small> <small class="error">This field is required.</small>
@ -34,14 +39,12 @@
</mat-form-field> </mat-form-field>
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<button <button mat-button
mat-button
[disabled]="waitForResponse || formGroup.invalid" [disabled]="waitForResponse || formGroup.invalid"
(click)="add()"> (click)="add()">
<mat-icon>add</mat-icon>Add <mat-icon>add</mat-icon>Add
</button> </button>
<button <button mat-button
mat-button
[disabled]="waitForResponse" [disabled]="waitForResponse"
(click)="cancel()"> (click)="cancel()">
<mat-icon>cancel</mat-icon>Cancel <mat-icon>cancel</mat-icon>Cancel

View File

@ -14,7 +14,8 @@
<small class="muted block">polic{{item().chatters.length == 1 ? 'y' : 'ies'}}</small> <small class="muted block">polic{{item().chatters.length == 1 ? 'y' : 'ies'}}</small>
</section> </section>
<section> <section>
<button mat-button (click)="router.navigate([link])"> <button mat-button
(click)="router.navigate([link])">
<mat-icon>pageview</mat-icon>View <mat-icon>pageview</mat-icon>View
</button> </button>
</section> </section>

View File

@ -8,7 +8,10 @@
{{policies.length}} polic{{policies.length == 1 ? 'y' : 'ies'}} {{policies.length}} polic{{policies.length == 1 ? 'y' : 'ies'}}
</mat-panel-description> </mat-panel-description>
</mat-expansion-panel-header> </mat-expansion-panel-header>
<policy-add-button class="add" [groups]="groups" [policies]="policies" [group]="group?.id" /> <policy-add-button class="add"
[groups]="groups"
[policies]="policies"
[group]="group?.id" />
@if (policies.length > 0) { @if (policies.length > 0) {
<policy-table [policies]="policies" /> <policy-table [policies]="policies" />
} }
@ -28,7 +31,9 @@
<p>Deleting this group will delete everything that is part of it, including policies and permissions.</p> <p>Deleting this group will delete everything that is part of it, including policies and permissions.</p>
</article> </article>
<article class="right"> <article class="right">
<button mat-raised-button class="delete" (click)="delete()"> <button mat-raised-button
class="delete"
(click)="delete()">
<mat-icon>delete</mat-icon>Delete this group. <mat-icon>delete</mat-icon>Delete this group.
</button> </button>
</article> </article>

View File

@ -1,15 +1,21 @@
<button mat-button [mat-menu-trigger-for]="menu"> <button mat-button
[mat-menu-trigger-for]="menu">
<mat-icon>add</mat-icon> <mat-icon>add</mat-icon>
Add a group Add a group
</button> </button>
<mat-menu #menu="matMenu"> <mat-menu #menu="matMenu">
<button mat-menu-item (click)="openDialog('')">Custom Group</button> <button mat-menu-item
<button mat-menu-item (click)="openDialog('everyone')">Everyone Group</button> (click)="openDialog('')">Custom Group</button>
<button mat-menu-item (click)="openDialog('subscribers')">Subscriber Group</button> <button mat-menu-item
<button mat-menu-item (click)="openDialog('moderators')">Moderator Group</button> (click)="openDialog('everyone')">Everyone Group</button>
<button mat-menu-item (click)="openDialog('vip')">VIP Group</button> <button mat-menu-item
<button mat-menu-item (click)="openDialog('broadcaster')">Broadcaster Group</button> (click)="openDialog('subscribers')">Subscriber Group</button>
<button mat-menu-item
(click)="openDialog('moderators')">Moderator Group</button>
<button mat-menu-item
(click)="openDialog('vip')">VIP Group</button>
<button mat-menu-item
(click)="openDialog('broadcaster')">Broadcaster Group</button>
</mat-menu> </mat-menu>
<group-list <group-list class="groups"
class="groups"
[groups]="items" /> [groups]="items" />

View File

@ -3,42 +3,49 @@
<ul> <ul>
@if (!isLoggedIn()) { @if (!isLoggedIn()) {
<li> <li>
<a routerLink="/login" routerLinkActive="active"> <a routerLink="/login"
routerLinkActive="active">
Login Login
</a> </a>
</li> </li>
} }
@if (isLoggedIn() && !isTTSLoggedIn()) { @if (isLoggedIn() && !isTTSLoggedIn()) {
<li> <li>
<a routerLink="/tts-login" routerLinkActive="active"> <a routerLink="/tts-login"
routerLinkActive="active">
TTS Login TTS Login
</a> </a>
</li> </li>
} }
@if (isLoggedIn() && isTTSLoggedIn()) { @if (isLoggedIn() && isTTSLoggedIn()) {
<li> <li>
<a routerLink="/policies" routerLinkActive="active"> <a routerLink="/policies"
routerLinkActive="active">
Policies Policies
</a> </a>
</li> </li>
<li> <li>
<a routerLink="/filters" routerLinkActive="active"> <a routerLink="/filters"
routerLinkActive="active">
Filters Filters
</a> </a>
</li> </li>
<li> <li>
<a routerLink="/actions" routerLinkActive="active"> <a routerLink="/actions"
routerLinkActive="active">
Actions Actions
</a> </a>
</li> </li>
<li> <li>
<a routerLink="/redemptions" routerLinkActive="active"> <a routerLink="/redemptions"
routerLinkActive="active">
Redemptions Redemptions
</a> </a>
</li> </li>
@if (isAdmin()) { @if (isAdmin()) {
<li> <li>
<a routerLink="/groups" routerLinkActive="active"> <a routerLink="/groups"
routerLinkActive="active">
Groups Groups
</a> </a>
</li> </li>

View File

@ -3,6 +3,7 @@ import { PolicyComponent } from './policy/policy.component';
import { PolicyTableComponent } from './policy-table/policy-table.component'; import { PolicyTableComponent } from './policy-table/policy-table.component';
import { PolicyItemEditComponent } from './policy-item-edit/policy-item-edit.component'; import { PolicyItemEditComponent } from './policy-item-edit/policy-item-edit.component';
import { PolicyAddButtonComponent } from './policy-add-button/policy-add-button.component'; import { PolicyAddButtonComponent } from './policy-add-button/policy-add-button.component';
import { PolicyAddFormComponent } from './policy-add-form/policy-add-form.component';
@NgModule({ @NgModule({
@ -12,6 +13,7 @@ import { PolicyAddButtonComponent } from './policy-add-button/policy-add-button.
PolicyTableComponent, PolicyTableComponent,
PolicyAddButtonComponent, PolicyAddButtonComponent,
PolicyItemEditComponent, PolicyItemEditComponent,
PolicyAddFormComponent,
] ]
}) })
export class PoliciesModule { } export class PoliciesModule { }

View File

@ -1,4 +1,5 @@
<button mat-button (click)="openDialog()"> <button mat-button
(click)="openDialog()">
<mat-icon>add</mat-icon> <mat-icon>add</mat-icon>
Add a policy Add a policy
</button> </button>

View File

@ -1,14 +1,12 @@
<div> <form standalone>
<form
standalone>
<mat-form-field> <mat-form-field>
<input <mat-label>Path</mat-label>
name="path" <input name="path"
type="text" type="text"
placeholder="Pick one" placeholder="Pick one"
[(ngModel)]="newPolicyName" [(ngModel)]="policy"
matInput matInput
[formControl]="myControl" [formControl]="policyControl"
[matAutocomplete]="auto" /> [matAutocomplete]="auto" />
<mat-autocomplete #auto="matAutocomplete"> <mat-autocomplete #auto="matAutocomplete">
@for (option of filteredPolicies | async; track option) { @for (option of filteredPolicies | async; track option) {
@ -16,10 +14,4 @@
} }
</mat-autocomplete> </mat-autocomplete>
</mat-form-field> </mat-form-field>
<button
mat-flat-button
(click)="addNewPolicy()">
Add
</button>
</form> </form>
</div>

View File

@ -4,9 +4,7 @@ import { FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms'
import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatAutocompleteModule } from '@angular/material/autocomplete';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { MatInputModule } from '@angular/material/input'; import { MatInputModule } from '@angular/material/input';
import EventService from '../../shared/services/EventService';
import { map, Observable, startWith } from 'rxjs'; import { map, Observable, startWith } from 'rxjs';
import { HermesClientService } from '../../hermes-client.service';
const Policies = [ const Policies = [
{ path: "tts", description: "Anything to do with TTS" }, { path: "tts", description: "Anything to do with TTS" },
@ -43,19 +41,19 @@ const Policies = [
styleUrl: './policy-add-form.component.scss' styleUrl: './policy-add-form.component.scss'
}) })
export class PolicyAddFormComponent { export class PolicyAddFormComponent {
myControl = new FormControl(''); policyControl = new FormControl('');
newPolicyName: string = ''; policy: string = '';
filteredPolicies: Observable<string[]>; filteredPolicies: Observable<string[]>;
constructor(private events: EventService, private hermes: HermesClientService) { constructor() {
this.filteredPolicies = this.myControl.valueChanges.pipe( this.filteredPolicies = this.policyControl.valueChanges.pipe(
startWith(''), startWith(''),
map(value => this._filter(value || '')), map(value => this._filter(value || '')),
); );
} }
ngOnInit() { ngOnInit() {
this.filteredPolicies = this.myControl.valueChanges.pipe( this.filteredPolicies = this.policyControl.valueChanges.pipe(
startWith(''), startWith(''),
map(value => this._filter(value || '')), map(value => this._filter(value || '')),
); );
@ -63,12 +61,11 @@ export class PolicyAddFormComponent {
private _filter(value: string): string[] { private _filter(value: string): string[] {
const filterValue = value.toLowerCase(); const filterValue = value.toLowerCase();
const names = Policies.map(p => p.path);
return Policies.map(p => p.path).filter(option => option.toLowerCase().includes(filterValue)); if (names.includes(filterValue)) {
return names;
} }
addNewPolicy() { return names.filter(option => option.toLowerCase().includes(filterValue));
this.events.emit('addPolicy', this.newPolicyName);
this.newPolicyName = "";
} }
} }

View File

@ -3,8 +3,7 @@
<mat-card-title>{{isNew ? 'Add' : 'Edit'}} Policy</mat-card-title> <mat-card-title>{{isNew ? 'Add' : 'Edit'}} Policy</mat-card-title>
</mat-card-header> </mat-card-header>
<mat-card-content> <mat-card-content>
<group-dropdown <group-dropdown ngDefaultControl
ngDefaultControl
[formControl]="groupControl" [formControl]="groupControl"
[groups]="data.groups" [groups]="data.groups"
[group]="data.group_id" [group]="data.group_id"
@ -12,7 +11,9 @@
[errorMessages]="groupErrorMessages" /> [errorMessages]="groupErrorMessages" />
<mat-form-field> <mat-form-field>
<mat-label>Path</mat-label> <mat-label>Path</mat-label>
<input matInput placeholder="Path" [formControl]="pathControl" /> <input matInput
placeholder="Path"
[formControl]="pathControl" />
@if (pathControl.invalid && (pathControl.dirty || pathControl.touched)) { @if (pathControl.invalid && (pathControl.dirty || pathControl.touched)) {
@if (pathControl.hasError('required')) { @if (pathControl.hasError('required')) {
<small class="error">This field is required.</small> <small class="error">This field is required.</small>
@ -21,7 +22,9 @@
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<mat-label>Usage</mat-label> <mat-label>Usage</mat-label>
<input matInput type="number" [formControl]="usageControl" /> <input matInput
type="number"
[formControl]="usageControl" />
@if (usageControl.invalid && (usageControl.dirty || usageControl.touched)) { @if (usageControl.invalid && (usageControl.dirty || usageControl.touched)) {
@if (usageControl.hasError('required')) { @if (usageControl.hasError('required')) {
<small class="error">This field is required.</small> <small class="error">This field is required.</small>
@ -39,7 +42,9 @@
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<mat-label>Span</mat-label> <mat-label>Span</mat-label>
<input matInput type="number" [formControl]="spanControl" /> <input matInput
type="number"
[formControl]="spanControl" />
@if (spanControl.invalid && (spanControl.dirty || spanControl.touched)) { @if (spanControl.invalid && (spanControl.dirty || spanControl.touched)) {
@if (spanControl.hasError('required')) { @if (spanControl.hasError('required')) {
<small class="error">This field is required.</small> <small class="error">This field is required.</small>
@ -58,15 +63,18 @@
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
@if (isNew) { @if (isNew) {
<button mat-button (click)="save()"> <button mat-button
(click)="save()">
<mat-icon>add</mat-icon>Add <mat-icon>add</mat-icon>Add
</button> </button>
} @else { } @else {
<button mat-button (click)="save()"> <button mat-button
(click)="save()">
<mat-icon>save</mat-icon>Save <mat-icon>save</mat-icon>Save
</button> </button>
} }
<button mat-button (click)="dialogRef.close()"> <button mat-button
(click)="dialogRef.close()">
<mat-icon>cancel</mat-icon>Cancel <mat-icon>cancel</mat-icon>Cancel
</button> </button>
</mat-card-actions> </mat-card-actions>

View File

@ -10,6 +10,7 @@ import { HermesClientService } from '../../hermes-client.service';
import { GroupDropdownComponent } from '../../groups/group-dropdown/group-dropdown.component'; import { GroupDropdownComponent } from '../../groups/group-dropdown/group-dropdown.component';
import { Group } from '../../shared/models/group'; import { Group } from '../../shared/models/group';
import { Policy } from '../../shared/models/policy'; import { Policy } from '../../shared/models/policy';
import { PolicyAddFormComponent } from '../policy-add-form/policy-add-form.component';
@Component({ @Component({
selector: 'policy-item-edit', selector: 'policy-item-edit',
@ -21,6 +22,7 @@ import { Policy } from '../../shared/models/policy';
MatIconModule, MatIconModule,
MatInputModule, MatInputModule,
ReactiveFormsModule, ReactiveFormsModule,
PolicyAddFormComponent
], ],
templateUrl: './policy-item-edit.component.html', templateUrl: './policy-item-edit.component.html',
styleUrl: './policy-item-edit.component.scss' styleUrl: './policy-item-edit.component.scss'

View File

@ -1,40 +1,59 @@
<table mat-table [dataSource]="policies" class="mat-elevation-z8"> <table mat-table
[dataSource]="policies"
class="mat-elevation-z8">
<ng-container matColumnDef="path"> <ng-container matColumnDef="path">
<th mat-header-cell *matHeaderCellDef>Path</th> <th mat-header-cell
<td mat-cell *matCellDef="let policy"> *matHeaderCellDef>Path</th>
<td mat-cell
*matCellDef="let policy">
{{policy.path}} {{policy.path}}
</td> </td>
</ng-container> </ng-container>
<ng-container matColumnDef="group"> <ng-container matColumnDef="group">
<th mat-header-cell *matHeaderCellDef>Group</th> <th mat-header-cell
<td mat-cell *matCellDef="let policy"> *matHeaderCellDef>Group</th>
<td mat-cell
*matCellDef="let policy">
{{getGroupById(policy.group_id)?.name || '\<unknown group\>'}} {{getGroupById(policy.group_id)?.name || '\<unknown group\>'}}
</td> </td>
</ng-container> </ng-container>
<ng-container matColumnDef="usage"> <ng-container matColumnDef="usage">
<th mat-header-cell *matHeaderCellDef>Usage Rate</th> <th mat-header-cell
<td mat-cell class="center" *matCellDef="let policy"> *matHeaderCellDef>Usage Rate</th>
<td mat-cell
class="center"
*matCellDef="let policy">
{{policy.usage}} {{policy.usage}}
</td> </td>
</ng-container> </ng-container>
<ng-container matColumnDef="span"> <ng-container matColumnDef="span">
<th mat-header-cell *matHeaderCellDef>Span (ms)</th> <th mat-header-cell
<td mat-cell class="center" *matCellDef="let policy"> *matHeaderCellDef>Span (ms)</th>
<td mat-cell
class="center"
*matCellDef="let policy">
{{policy.span}} {{policy.span}}
</td> </td>
</ng-container> </ng-container>
<ng-container matColumnDef="actions"> <ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef> Actions </th> <th mat-header-cell
<td mat-cell *matCellDef="let policy"> *matHeaderCellDef> Actions </th>
<button mat-button (click)="edit(policy)"><mat-icon>edit</mat-icon>Edit</button> <td mat-cell
<button mat-button class="delete" (click)="delete(policy)"><mat-icon>delete</mat-icon>Delete</button> *matCellDef="let policy">
<button mat-button
(click)="edit(policy)"><mat-icon>edit</mat-icon>Edit</button>
<button mat-button
class="delete"
(click)="delete(policy)"><mat-icon>delete</mat-icon>Delete</button>
</td> </td>
</ng-container> </ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> <tr mat-header-row
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr> *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row
*matRowDef="let row; columns: displayedColumns;"></tr>
</table> </table>

View File

@ -1,6 +1,8 @@
<h4>Policies</h4> <h4>Policies</h4>
<div class="add"> <div class="add">
<policy-add-button [policies]="policies" [groups]="groups" (policy)="addPolicy($event)" /> <policy-add-button [policies]="policies"
[groups]="groups"
(policy)="addPolicy($event)" />
</div> </div>
<div> <div>

View File

@ -5,15 +5,13 @@
</mat-card-title> </mat-card-title>
</mat-card-header> </mat-card-header>
<mat-card-content> <mat-card-content>
<twitch-redemption-dropdown <twitch-redemption-dropdown ngDefaultControl
ngDefaultControl
[formControl]="redemptionFormControl" [formControl]="redemptionFormControl"
[errorMessages]="redemptionErrorMessages" [errorMessages]="redemptionErrorMessages"
[twitchRedemptions]="twitchRedemptions" [twitchRedemptions]="twitchRedemptions"
[(twitchRedemptionId)]="redemption.redemption_id" /> [(twitchRedemptionId)]="redemption.redemption_id" />
<action-dropdown <action-dropdown ngDefaultControl
ngDefaultControl
[formControl]="actionFormControl" [formControl]="actionFormControl"
[errorMessages]="actionErrorMessages" [errorMessages]="actionErrorMessages"
[actions]="redeemableActions" [actions]="redeemableActions"
@ -21,7 +19,10 @@
<mat-form-field> <mat-form-field>
<mat-label>Order</mat-label> <mat-label>Order</mat-label>
<input matInput type="number" [formControl]="orderFormControl" [value]="redemption.order" /> <input matInput
type="number"
[formControl]="orderFormControl"
[value]="redemption.order" />
@if (orderFormControl.invalid && (orderFormControl.dirty || orderFormControl.touched)) { @if (orderFormControl.invalid && (orderFormControl.dirty || orderFormControl.touched)) {
@for (error of orderErrorMessageKeys; track $index) { @for (error of orderErrorMessageKeys; track $index) {
@if (orderFormControl.hasError(error)) { @if (orderFormControl.hasError(error)) {
@ -31,16 +32,14 @@
} }
</mat-form-field> </mat-form-field>
<div class="buttons"> <div class="buttons">
<button <button mat-icon-button
mat-icon-button
class="save" class="save"
[disabled]="waitForResponse || formGroups.invalid" [disabled]="waitForResponse || formGroups.invalid"
(click)="save()"> (click)="save()">
<mat-icon>save</mat-icon> <mat-icon>save</mat-icon>
</button> </button>
@if (redemption.id) { @if (redemption.id) {
<button <button mat-icon-button
mat-icon-button
class="delete" class="delete"
[disabled]="waitForResponse" [disabled]="waitForResponse"
(click)="delete()"> (click)="delete()">

View File

@ -1,7 +1,10 @@
<div class="content"> <div class="content">
<button mat-button class="add" (click)="add()"><mat-icon>add</mat-icon> Add Redemption</button> <button mat-button
class="add"
(click)="add()"><mat-icon>add</mat-icon> Add Redemption</button>
<mat-expansion-panel class="filters-expander" (opened)="panelOpenState.set(true)" <mat-expansion-panel class="filters-expander"
(opened)="panelOpenState.set(true)"
(closed)="panelOpenState.set(false)"> (closed)="panelOpenState.set(false)">
<mat-expansion-panel-header> <mat-expansion-panel-header>
<mat-panel-title>Filters</mat-panel-title> <mat-panel-title>Filters</mat-panel-title>
@ -11,40 +14,55 @@
</mat-expansion-panel-header> </mat-expansion-panel-header>
<div class="filters"> <div class="filters">
<twitch-redemption-dropdown [(twitchRedemptionId)]="filter_redemption" [search]="true" /> <twitch-redemption-dropdown [(twitchRedemptionId)]="filter_redemption"
<action-dropdown [(action)]="filter_action_name" [search]="true" /> [search]="true" />
<action-dropdown [(action)]="filter_action_name"
[search]="true" />
</div> </div>
</mat-expansion-panel> </mat-expansion-panel>
<div class="table-container"> <div class="table-container">
<table mat-table [dataSource]="redemptions" class="mat-elevation-z8"> <table mat-table
[dataSource]="redemptions"
class="mat-elevation-z8">
<ng-container matColumnDef="twitch-redemption"> <ng-container matColumnDef="twitch-redemption">
<th mat-header-cell *matHeaderCellDef>Twitch Redemption Name</th> <th mat-header-cell
<td mat-cell *matCellDef="let redemption">{{getTwitchRedemptionNameById(redemption.redemption_id) || 'Unknown *matHeaderCellDef>Twitch Redemption Name</th>
<td mat-cell
*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">
<th mat-header-cell *matHeaderCellDef>Action Name</th> <th mat-header-cell
<td mat-cell *matCellDef="let redemption">{{redemption.action_name}}</td> *matHeaderCellDef>Action Name</th>
<td mat-cell
*matCellDef="let redemption">{{redemption.action_name}}</td>
</ng-container> </ng-container>
<ng-container matColumnDef="order"> <ng-container matColumnDef="order">
<th mat-header-cell *matHeaderCellDef>Order</th> <th mat-header-cell
<td mat-cell *matCellDef="let redemption">{{redemption.order}}</td> *matHeaderCellDef>Order</th>
<td mat-cell
*matCellDef="let redemption">{{redemption.order}}</td>
</ng-container> </ng-container>
<ng-container matColumnDef="misc"> <ng-container matColumnDef="misc">
<th mat-header-cell *matHeaderCellDef></th> <th mat-header-cell
<td mat-cell *matCellDef="let redemption"> *matHeaderCellDef></th>
<button mat-icon-button (click)="openDialog(redemption)"> <td mat-cell
*matCellDef="let redemption">
<button mat-icon-button
(click)="openDialog(redemption)">
<mat-icon>edit</mat-icon> <mat-icon>edit</mat-icon>
</button> </button>
</td> </td>
</ng-container> </ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></tr> <tr mat-header-row
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr> *matHeaderRowDef="displayedColumns; sticky: true"></tr>
<tr mat-row
*matRowDef="let row; columns: displayedColumns;"></tr>
</table> </table>
</div> </div>
</div> </div>

View File

@ -1,8 +1,16 @@
<mat-form-field> <mat-form-field>
<mat-label>Twitch Redemption</mat-label> <mat-label>Twitch Redemption</mat-label>
<input type="text" matInput placeholder="Pick a Twitch redemption" aria-label="twitch redemption" <input type="text"
[formControl]="formControl" [matAutocomplete]="auto" (blur)="blur()" (input)="input()"> matInput
<mat-autocomplete #auto="matAutocomplete" [displayWith]="displayFn" (optionSelected)="select($event.option.value)"> placeholder="Pick a Twitch redemption"
aria-label="twitch redemption"
[formControl]="formControl"
[matAutocomplete]="auto"
(blur)="blur()"
(input)="input()">
<mat-autocomplete #auto="matAutocomplete"
[displayWith]="displayFn"
(optionSelected)="select($event.option.value)">
@for (redemption of filteredRedemptions; track redemption.id) { @for (redemption of filteredRedemptions; track redemption.id) {
<mat-option [value]="redemption">{{redemption.title}}</mat-option> <mat-option [value]="redemption">{{redemption.title}}</mat-option>
} }

View File

@ -6,7 +6,10 @@
<div> <div>
<mat-form-field> <mat-form-field>
<mat-label>Search</mat-label> <mat-label>Search</mat-label>
<input matInput cdkFocusInitial type="text" formControlName="search" /> <input matInput
cdkFocusInitial
type="text"
formControlName="search" />
@if (forms.get('search')?.invalid && (forms.get('search')?.dirty || forms.get('search')?.touched)) { @if (forms.get('search')?.invalid && (forms.get('search')?.dirty || forms.get('search')?.touched)) {
<small class="error">Search is required.</small> <small class="error">Search is required.</small>
} }
@ -15,13 +18,16 @@
<div> <div>
<mat-form-field> <mat-form-field>
<mat-label>Replace</mat-label> <mat-label>Replace</mat-label>
<input matInput formControlName="replace" /> <input matInput
formControlName="replace" />
</mat-form-field> </mat-form-field>
</div> </div>
<div> <div>
<mat-form-field> <mat-form-field>
<mat-label>Regex Options</mat-label> <mat-label>Regex Options</mat-label>
<mat-select multiple [formControl]="flagControl" [compareWith]="compare" <mat-select multiple
[formControl]="flagControl"
[compareWith]="compare"
(selectionChange)="onSelectionChange($event)"> (selectionChange)="onSelectionChange($event)">
<mat-select-trigger> <mat-select-trigger>
{{optionsSelected[0] || ''}} {{optionsSelected[0] || ''}}
@ -43,11 +49,9 @@
</form> </form>
</mat-dialog-content> </mat-dialog-content>
<mat-dialog-actions> <mat-dialog-actions>
<button <button mat-button
mat-button
(click)="onCancelClick()">Cancel</button> (click)="onCancelClick()">Cancel</button>
<button <button mat-button
mat-button
(click)="onSaveClick()" (click)="onSaveClick()"
[disabled]="!forms.dirty || forms.invalid || waitForResponse">Save</button> [disabled]="!forms.dirty || forms.invalid || waitForResponse">Save</button>
</mat-dialog-actions> </mat-dialog-actions>

View File

@ -11,11 +11,15 @@
</li> </li>
<li> <li>
<mat-menu #filterMenu> <mat-menu #filterMenu>
<button mat-menu-item (click)="openDialog()">Edit</button> <button mat-menu-item
<button mat-menu-item (click)="onDelete.emit(item)">Delete</button> (click)="openDialog()">Edit</button>
<button mat-menu-item
(click)="onDelete.emit(item)">Delete</button>
</mat-menu> </mat-menu>
<button mat-icon-button class="small-button" [matMenuTriggerFor]="filterMenu"> <button mat-icon-button
class="small-button"
[matMenuTriggerFor]="filterMenu">
<mat-icon>more_vert</mat-icon> <mat-icon>more_vert</mat-icon>
</button> </button>
</li> </li>

View File

@ -9,8 +9,7 @@
</li> </li>
@for (filter of filters; track $index) { @for (filter of filters; track $index) {
<li> <li>
<tts-filter-item <tts-filter-item [item]="filter"
[item]="filter"
(onDelete)="deleteFilter($event)" /> (onDelete)="deleteFilter($event)" />
</li> </li>
} }

View File

@ -2,13 +2,15 @@
<article> <article>
<h3>Filters</h3> <h3>Filters</h3>
<div> <div>
<button mat-button class="add" aria-label="Add a filter" (click)="openDialog()"> <button mat-button
class="add"
aria-label="Add a filter"
(click)="openDialog()">
<mat-icon>add</mat-icon>Add TTS Filter <mat-icon>add</mat-icon>Add TTS Filter
</button> </button>
</div> </div>
</article> </article>
<div class="grow"> <div class="grow">
<tts-filter-list <tts-filter-list [filters]="items" />
[filters]="items" />
</div> </div>
</main> </main>

View File

@ -1,15 +1,23 @@
<!doctype html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>Tom-to-Speech</title> <title>Tom-to-Speech</title>
<base href="/"> <base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport"
<link rel="icon" type="image/x-icon" href="favicon.ico"> content="width=device-width, initial-scale=1">
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet"> <link rel="icon"
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"> type="image/x-icon"
href="favicon.ico">
<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> </head>
<body class="mat-typography"> <body class="mat-typography">
<app-root></app-root> <app-root></app-root>
</body> </body>
</html> </html>

View File

@ -1,7 +1,14 @@
/* You can add global styles to this file, and also import other style files */ /* You can add global styles to this file, and also import other style files */
html, body { height: 100%; } html,
body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; } body {
height: 100%;
}
body {
margin: 0;
font-family: Roboto, "Helvetica Neue", sans-serif;
}
.mat-mdc-dialog-surface { .mat-mdc-dialog-surface {
background-color: transparent !important; background-color: transparent !important;