For modals, response error messages is shown in the footer. For modals, Accept button is always on the right.

This commit is contained in:
Tom 2025-03-20 13:14:09 +00:00
parent 1acda7978e
commit c8bfeed396
16 changed files with 120 additions and 41 deletions

View File

@ -121,5 +121,11 @@
disabled="{{!formsDirty || !formsValidity || waitForResponse}}" disabled="{{!formsDirty || !formsValidity || waitForResponse}}"
(click)="save()">Save</button> (click)="save()">Save</button>
</mat-card-actions> </mat-card-actions>
@if (responseError) {
<mat-card-footer>
<small class="error below">{{responseError}}</small>
</mat-card-footer>
}
</mat-card> </mat-card>
</body> </body>

View File

@ -14,6 +14,14 @@
color: #ba1a1a; color: #ba1a1a;
} }
.below {
display: block;
justify-self: center;
align-items: center;
align-self: center;
text-align: center;
}
.actions { .actions {
display: flex; display: flex;
flex-direction: row; flex-direction: row;

View File

@ -203,6 +203,7 @@ export class ActionItemEditComponent implements OnInit {
isNew: boolean = true; isNew: boolean = true;
previousName: string = this.action.name; previousName: string = this.action.name;
responseError: string | undefined;
waitForResponse: boolean = false; waitForResponse: boolean = false;
readonly formGroup = new FormGroup({ readonly formGroup = new FormGroup({
@ -239,16 +240,17 @@ export class ActionItemEditComponent implements OnInit {
return; return;
this.waitForResponse = true; this.waitForResponse = true;
this.responseError = undefined;
this.client.first((d: any) => d.op == 4 && d.d.request.type == 'delete_redeemable_action' && d.d.request.data.name == this.action.name) this.client.first((d: any) => d.op == 4 && d.d.request.type == 'delete_redeemable_action' && d.d.request.data.name == this.action.name)
.subscribe({ .subscribe({
next: (d) => { next: (d) => {
if (d.d.error) { if (d.d.error) {
// TODO: update & show response error message. this.responseError = d.d.error;
} else { } else {
this.dialogRef.close(); this.dialogRef.close();
} }
}, },
error: () => this.waitForResponse = false, error: () => this.responseError = 'Something went wrong.',
complete: () => this.waitForResponse = false, complete: () => this.waitForResponse = false,
}); });
this.client.deleteRedeemableAction(action.name); this.client.deleteRedeemableAction(action.name);
@ -260,6 +262,7 @@ export class ActionItemEditComponent implements OnInit {
} }
this.waitForResponse = true; this.waitForResponse = true;
this.responseError = undefined;
const fields = this.actionEntries[this.action.type]; const fields = this.actionEntries[this.action.type];
if (fields.some(f => f.control.invalid)) { if (fields.some(f => f.control.invalid)) {
@ -285,12 +288,12 @@ export class ActionItemEditComponent implements OnInit {
.subscribe({ .subscribe({
next: (d) => { next: (d) => {
if (d.d.error) { if (d.d.error) {
// TODO: update & show response error message. this.responseError = d.d.error;
} else { } else {
this.dialogRef.close(this.action); this.dialogRef.close(this.action);
} }
}, },
error: () => this.waitForResponse = false, error: () => this.responseError = 'Something went wrong.',
complete: () => this.waitForResponse = false, complete: () => this.waitForResponse = false,
}); });
if (isNewAction) if (isNewAction)

View File

@ -39,15 +39,21 @@
</mat-form-field> </mat-form-field>
</mat-card-content> </mat-card-content>
<mat-card-actions align="end"> <mat-card-actions align="end">
<button mat-button
[disabled]="waitForResponse || formGroup.invalid"
(click)="add()">
<mat-icon>add</mat-icon>Add
</button>
<button mat-button <button mat-button
[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
[disabled]="waitForResponse || formGroup.invalid"
(click)="add()">
<mat-icon>add</mat-icon>Add
</button>
</mat-card-actions> </mat-card-actions>
@if (responseError) {
<mat-card-footer>
<small class="error below">{{responseError}}</small>
</mat-card-footer>
}
</mat-card> </mat-card>

View File

@ -5,4 +5,17 @@
.mat-mdc-card-actions { .mat-mdc-card-actions {
align-self: center; align-self: center;
}
.error {
display: block;
color: #ba1a1a;
}
.below {
display: block;
justify-self: center;
align-items: center;
align-self: center;
text-align: center;
} }

View File

@ -30,6 +30,7 @@ export class GroupItemEditComponent implements OnInit {
group: Group = { id: '', user_id: '', name: '', priority: 0 }; group: Group = { id: '', user_id: '', name: '', priority: 0 };
isSpecial: boolean = false; isSpecial: boolean = false;
responseError: string | undefined;
waitForResponse: boolean = false; waitForResponse: boolean = false;
nameForm = new FormControl('', [Validators.required]); nameForm = new FormControl('', [Validators.required]);
@ -53,16 +54,17 @@ export class GroupItemEditComponent implements OnInit {
return; return;
this.waitForResponse = true; this.waitForResponse = true;
this.responseError = undefined;
this._client.first((d: any) => d.op == 4 && d.d.request.type == 'create_group' && d.d.data.name == this.nameForm.value) this._client.first((d: any) => d.op == 4 && d.d.request.type == 'create_group' && d.d.data.name == this.nameForm.value)
.subscribe({ .subscribe({
next: (d) => { next: (d) => {
if (d.d.error) { if (d.d.error) {
// TODO: update & show response error message. this.responseError = d.d.error;
} else { } else {
this._dialogRef.close(d.d.data); this._dialogRef.close(d.d.data);
} }
}, },
error: () => this.waitForResponse = false, error: () => this.responseError = 'Something went wrong.',
complete: () => this.waitForResponse = false, complete: () => this.waitForResponse = false,
}); });
this._client.createGroup(this.nameForm.value!, this.priorityForm.value!); this._client.createGroup(this.nameForm.value!, this.priorityForm.value!);

View File

@ -1,3 +1,4 @@
.error { .error {
display: block;
color: #ba1a1a; color: #ba1a1a;
} }

View File

@ -9,7 +9,8 @@
[group]="data.group_id" [group]="data.group_id"
[groupDisabled]="data.groupDisabled" [groupDisabled]="data.groupDisabled"
[errorMessages]="groupErrorMessages" /> [errorMessages]="groupErrorMessages" />
<policy-dropdown #policyDropdown [policy]="pathControl.value" /> <policy-dropdown #policyDropdown
[policy]="pathControl.value" />
<mat-form-field> <mat-form-field>
<mat-label>Usage</mat-label> <mat-label>Usage</mat-label>
<input matInput <input matInput
@ -52,6 +53,10 @@
</mat-form-field> </mat-form-field>
</mat-card-content> </mat-card-content>
<mat-card-actions align="end"> <mat-card-actions align="end">
<button mat-button
(click)="dialogRef.close()">
<mat-icon>cancel</mat-icon>Cancel
</button>
@if (isNew) { @if (isNew) {
<button mat-button <button mat-button
(click)="save()"> (click)="save()">
@ -63,9 +68,11 @@
<mat-icon>save</mat-icon>Save <mat-icon>save</mat-icon>Save
</button> </button>
} }
<button mat-button
(click)="dialogRef.close()">
<mat-icon>cancel</mat-icon>Cancel
</button>
</mat-card-actions> </mat-card-actions>
@if (responseError) {
<mat-card-footer>
<small class="error below">{{responseError}}</small>
</mat-card-footer>
}
</mat-card> </mat-card>

View File

@ -6,5 +6,14 @@
} }
.error { .error {
display: block;
color: #ba1a1a; color: #ba1a1a;
}
.below {
display: block;
justify-self: center;
align-items: center;
align-self: center;
text-align: center;
} }

View File

@ -49,6 +49,7 @@ export class PolicyItemEditComponent implements OnInit, AfterViewInit {
}); });
isNew: boolean = false; isNew: boolean = false;
responseError: string | undefined;
waitForResponse: boolean = false; waitForResponse: boolean = false;
@ViewChild('policyDropdown') policyDropdown: PolicyDropdownComponent | undefined; @ViewChild('policyDropdown') policyDropdown: PolicyDropdownComponent | undefined;
@ -81,6 +82,7 @@ export class PolicyItemEditComponent implements OnInit, AfterViewInit {
return; return;
this.waitForResponse = true; this.waitForResponse = true;
this.responseError = undefined;
const group_id = (this.groupControl.value as Group)!.id; const group_id = (this.groupControl.value as Group)!.id;
const path = this.pathControl.value!; const path = this.pathControl.value!;
const usage = this.usageControl.value!; const usage = this.usageControl.value!;
@ -91,12 +93,12 @@ export class PolicyItemEditComponent implements OnInit, AfterViewInit {
.subscribe({ .subscribe({
next: (d) => { next: (d) => {
if (d.d.error) { if (d.d.error) {
// TODO: update & show response error message. this.responseError = d.d.error;
} else { } else {
this.dialogRef.close(d.d.data); this.dialogRef.close(d.d.data);
} }
}, },
error: () => this.waitForResponse = false, error: () => this.responseError = 'Something went wrong.',
complete: () => this.waitForResponse = false, complete: () => this.waitForResponse = false,
}); });
this.client.createPolicy(group_id, path, usage, span); this.client.createPolicy(group_id, path, usage, span);
@ -105,12 +107,12 @@ export class PolicyItemEditComponent implements OnInit, AfterViewInit {
.subscribe({ .subscribe({
next: (d) => { next: (d) => {
if (d.d.error) { if (d.d.error) {
// TODO: update & show response error message. this.responseError = d.d.error;
} else { } else {
this.dialogRef.close(d.d.data); this.dialogRef.close(d.d.data);
} }
}, },
error: () => this.waitForResponse = false, error: () => this.responseError = 'Something went wrong.',
complete: () => this.waitForResponse = false, complete: () => this.waitForResponse = false,
}); });
this.client.updatePolicy(this.data.policy_id, group_id, path, usage, span); this.client.updatePolicy(this.data.policy_id, group_id, path, usage, span);

View File

@ -95,7 +95,7 @@ export class RedemptionItemEditComponent implements OnInit {
this.dialogRef.close(id); this.dialogRef.close(id);
} }
}, },
error: () => { this.responseError = 'Failed to receive response back from server.'; this.waitForResponse = false; }, error: () => this.responseError = 'Something went wrong.',
complete: () => this.waitForResponse = false, complete: () => this.waitForResponse = false,
}); });
this.client.deleteRedemption(id); this.client.deleteRedemption(id);
@ -127,7 +127,7 @@ export class RedemptionItemEditComponent implements OnInit {
this.dialogRef.close(d.d.data); this.dialogRef.close(d.d.data);
} }
}, },
error: () => { this.responseError = 'Failed to receive response back from server.'; this.waitForResponse = false; }, error: () => this.responseError = 'Something went wrong.',
complete: () => this.waitForResponse = false, complete: () => this.waitForResponse = false,
}); });
this.client.createRedemption(this.redemption.redemption_id, this.redemption.action_name, order); this.client.createRedemption(this.redemption.redemption_id, this.redemption.action_name, order);
@ -142,7 +142,7 @@ export class RedemptionItemEditComponent implements OnInit {
this.dialogRef.close(d.d.data); this.dialogRef.close(d.d.data);
} }
}, },
error: () => { this.responseError = 'Failed to receive response back from server.'; this.waitForResponse = false; }, error: () => this.responseError = 'Something went wrong.',
complete: () => this.waitForResponse = false, complete: () => this.waitForResponse = false,
}); });
this.client.updateRedemption(this.redemption.id, this.redemption.redemption_id, this.redemption.action_name, order); this.client.updateRedemption(this.redemption.id, this.redemption.redemption_id, this.redemption.action_name, order);

View File

@ -43,8 +43,8 @@
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
</div> </div>
@if (errorMessage) { @if (responseError) {
<small class="error">{{errorMessage}}</small> <small class="error">{{responseError}}</small>
} }
</form> </form>
</mat-dialog-content> </mat-dialog-content>

View File

@ -62,7 +62,7 @@ export class FilterItemEditComponent {
}); });
waitForResponse: boolean = false; waitForResponse: boolean = false;
errorMessage: string | undefined; responseError: string | undefined;
get optionsSelected() { get optionsSelected() {
@ -75,7 +75,7 @@ export class FilterItemEditComponent {
return; return;
this.waitForResponse = true; this.waitForResponse = true;
this.errorMessage = undefined; this.responseError = undefined;
const search = this.searchControl.value!; const search = this.searchControl.value!;
const replace = this.replaceControl.value!; const replace = this.replaceControl.value!;
@ -86,11 +86,11 @@ export class FilterItemEditComponent {
?.subscribe({ ?.subscribe({
next: d => { next: d => {
if (d.error) if (d.error)
this.errorMessage = d.error; this.responseError = d.error;
else else
this.dialogRef.close(d.d.data); this.dialogRef.close(d.d.data);
}, },
error: () => this.waitForResponse = false, error: () => this.responseError = 'Something went wrong.',
complete: () => this.waitForResponse = false, complete: () => this.waitForResponse = false,
}); });
this.client.createTTSFilter(search, replace, flag); this.client.createTTSFilter(search, replace, flag);
@ -99,11 +99,11 @@ export class FilterItemEditComponent {
?.subscribe({ ?.subscribe({
next: d => { next: d => {
if (d.error) if (d.error)
this.errorMessage = d.error; this.responseError = d.error;
else else
this.dialogRef.close(d.d.data); this.dialogRef.close(d.d.data);
}, },
error: () => this.waitForResponse = false, error: () => this.responseError = 'Something went wrong.',
complete: () => this.waitForResponse = false, complete: () => this.waitForResponse = false,
}); });
this.client.updateTTSFilter(this.data.id, search, replace, flag); this.client.updateTTSFilter(this.data.id, search, replace, flag);

View File

@ -1,13 +1,14 @@
<mat-card> <mat-card>
<mat-card-header> <mat-card-header>
<mat-card-title-group> <mat-card-title-group>
<mat-card-title>Add Twitch User to Group</mat-card-title> <mat-card-title>Add Twitch User</mat-card-title>
<mat-card-subtitle>Adding to ...</mat-card-subtitle> <mat-card-subtitle>to {{data.group.name}}</mat-card-subtitle>
</mat-card-title-group> </mat-card-title-group>
</mat-card-header> </mat-card-header>
<mat-card-content> <mat-card-content>
<mat-form-field> <mat-form-field>
<mat-label>Twitch Username</mat-label>
<input matInput <input matInput
[formControl]="usernameControl" /> [formControl]="usernameControl" />
</mat-form-field> </mat-form-field>
@ -17,7 +18,13 @@
<button mat-raised-button <button mat-raised-button
(click)="dialogRef.close()">Cancel</button> (click)="dialogRef.close()">Cancel</button>
<button mat-raised-button <button mat-raised-button
disabled="{{waitForResponse}}" disabled="{{usernameControl.invalid || waitForResponse}}"
(click)="submit()">Add</button> (click)="submit()">Add</button>
</mat-card-actions> </mat-card-actions>
@if (responseError) {
<mat-card-footer>
<small class="error below">{{responseError}}</small>
</mat-card-footer>
}
</mat-card> </mat-card>

View File

@ -0,0 +1,16 @@
.mat-mdc-card-actions {
justify-content: space-between;
}
.error {
display: block;
color: #ba1a1a;
}
.below {
display: block;
justify-self: center;
align-items: center;
align-self: center;
text-align: center;
}

View File

@ -27,12 +27,13 @@ import { group } from 'console';
}) })
export class TwitchUserItemAddComponent implements OnInit { export class TwitchUserItemAddComponent implements OnInit {
private readonly client = inject(HermesClientService); private readonly client = inject(HermesClientService);
private readonly data = inject<{ username: string, group: Group }>(MAT_DIALOG_DATA);
private readonly http = inject(HttpClient); private readonly http = inject(HttpClient);
readonly data = inject<{ username: string, group: Group }>(MAT_DIALOG_DATA);
readonly usernameControl = new FormControl('', [Validators.required]); readonly usernameControl = new FormControl('', [Validators.required]);
readonly dialogRef = inject(MatDialogRef<ActionItemEditComponent>); readonly dialogRef = inject(MatDialogRef<ActionItemEditComponent>);
responseError: string | undefined;
waitForResponse = false; waitForResponse = false;
@ -46,6 +47,7 @@ export class TwitchUserItemAddComponent implements OnInit {
} }
this.waitForResponse = true; this.waitForResponse = true;
this.responseError = undefined;
const username = this.usernameControl.value!.toLowerCase(); const username = this.usernameControl.value!.toLowerCase();
this.http.get('/api/auth/twitch/users?login=' + username, { this.http.get('/api/auth/twitch/users?login=' + username, {
@ -56,10 +58,7 @@ export class TwitchUserItemAddComponent implements OnInit {
.subscribe((response: any) => { .subscribe((response: any) => {
if (!response.user) { if (!response.user) {
this.waitForResponse = false; this.waitForResponse = false;
return; this.responseError = 'Twitch username does not exist.';
}
if (!response.user) {
return; return;
} }
@ -67,12 +66,12 @@ export class TwitchUserItemAddComponent implements OnInit {
.subscribe({ .subscribe({
next: (d) => { next: (d) => {
if (d.d.error) { if (d.d.error) {
// TODO: update & show response error message. this.responseError = d.d.error;
} else { } else {
this.dialogRef.close(d.d.data); this.dialogRef.close(d.d.data);
} }
}, },
error: () => this.waitForResponse = false, error: () => this.responseError = 'Something went wrong.',
complete: () => this.waitForResponse = false, complete: () => this.waitForResponse = false,
}); });
this.client.createGroupChatter(this.data.group.id, response.user.id, response.user.login) this.client.createGroupChatter(this.data.group.id, response.user.id, response.user.login)