Skip to content

Commit a4c563b

Browse files
tanemluTanja Ulmen
andauthored
Fix 457 - Added admin user settings and added "hide usernames" into it (hobbyfarm#226)
* adding backend conversation for hideUsername button * fixing saving changes with settings modal * cleaned up files * deleted terminal theme from settings --------- Co-authored-by: Tanja Ulmen <[email protected]>
1 parent 55c1df9 commit a4c563b

File tree

6 files changed

+168
-12
lines changed

6 files changed

+168
-12
lines changed

src/app/configuration/settings/settings.component.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Component, OnChanges, OnInit, ViewChild } from '@angular/core';
1+
import { Component, ViewChild } from '@angular/core';
22
import { TypedInput, FormGroupType } from '../../typed-form/TypedInput';
33
import {
44
PreparedScope,
@@ -20,7 +20,7 @@ export class SettingsComponent {
2020
public valid: boolean = true;
2121
public scopes: PreparedScope[] = [];
2222
public selectedScope: PreparedScope;
23-
public loading: boolean = true;
23+
public loading: boolean = true;
2424
public scopesLoading: boolean = true;
2525
readonly FormGroupType = FormGroupType; // Reference to TypedInputTypes enum for template use
2626

src/app/dashboards/progress-dashboard/progress-dashboard.component.html

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@
2525
type="checkbox"
2626
clrToggle
2727
name="hideUsernames"
28-
[(ngModel)]="hideUsernames"
28+
[(ngModel)]="hide_usernames_status"
29+
(ngModelChange)="saveSettings($event)"
2930
/>
3031
<label>Hide Usernames</label>
3132
</clr-toggle-wrapper>
@@ -118,7 +119,7 @@
118119
<progress-card
119120
[progress]="p"
120121
[pause]="pause"
121-
[hideUsername]="hideUsernames"
122+
[hideUsername]="hide_usernames_status"
122123
(nameClickedEvent)="filterName($event)"
123124
></progress-card>
124125
</div>

src/app/dashboards/progress-dashboard/progress-dashboard.component.ts

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Component, OnInit, ViewChild, Input } from '@angular/core';
1+
import { Component, OnInit, ViewChild, Input, OnDestroy, OnChanges } from '@angular/core';
22
import { ProgressService } from 'src/app/data/progress.service';
33
import { Progress } from 'src/app/data/progress';
44
import { UserService } from '../../data/user.service';
@@ -8,15 +8,17 @@ import { ScenarioService } from '../../data/scenario.service';
88
import { CourseService } from '../../data/course.service';
99
import { EventUserListComponent } from './event-user-list/event-user-list.component';
1010
import { JwtHelperService } from '@auth0/angular-jwt';
11-
import { combineLatest } from 'rxjs';
11+
import { combineLatest, Subject, takeUntil } from 'rxjs';
1212
import { User } from '../../data/user';
13+
import { Settings, SettingsService } from 'src/app/data/settings.service';
14+
import { FormGroup } from '@angular/forms';
1315

1416
@Component({
1517
selector: 'progress-dashboard',
1618
templateUrl: './progress-dashboard.component.html',
1719
styleUrls: ['./progress-dashboard.component.scss'],
1820
})
19-
export class ProgressDashboardComponent implements OnInit {
21+
export class ProgressDashboardComponent implements OnInit, OnDestroy, OnChanges {
2022
@Input()
2123
selectedEvent: ScheduledEvent;
2224

@@ -26,7 +28,9 @@ export class ProgressDashboardComponent implements OnInit {
2628
public callInterval: any;
2729
public circleVisible: boolean = true;
2830
public users: User[];
29-
public hideUsernames: boolean = false;
31+
public settingsForm: FormGroup;
32+
public hide_usernames_status: boolean = false;
33+
private settings_service$ = new Subject<Readonly<Settings>>();
3034

3135
public pauseCall: boolean = false; // Stop refreshing if we are looking at a progress
3236
public pause = (pause: boolean) => {
@@ -48,17 +52,35 @@ export class ProgressDashboardComponent implements OnInit {
4852
public courseService: CourseService,
4953
public progressService: ProgressService,
5054
public scheduledeventService: ScheduledeventService,
51-
public helper: JwtHelperService
55+
public helper: JwtHelperService,
56+
public settingsService: SettingsService
5257
) {}
5358

5459
ngOnInit() {
60+
this.settingsForm = this.settingsService.getForm()
61+
this.settingsService.settings$
62+
.pipe(takeUntil(this.settings_service$))
63+
.subscribe(
64+
({
65+
hide_usernames_status = false,
66+
}) => {
67+
this.settingsForm.patchValue({
68+
hide_usernames_status
69+
});
70+
this.hide_usernames_status = this.settingsForm.get('hide_usernames_status')?.value
71+
},
72+
);
5573
this.refresh();
5674
}
5775

5876
ngOnChanges() {
5977
this.refresh();
6078
}
6179

80+
ngOnDestroy() {
81+
this.settings_service$.unsubscribe();
82+
}
83+
6284
filter() {
6385
if (this.userFilter != '') {
6486
try {
@@ -152,6 +174,19 @@ export class ProgressDashboardComponent implements OnInit {
152174
});
153175
}
154176

177+
saveSettings(newHideUsernamesStatus: boolean) {
178+
if (this.settingsForm.value) {
179+
this.settingsService.update({hide_usernames_status: newHideUsernamesStatus}).subscribe({
180+
next: () => {
181+
console.log('Saved Settings.');
182+
},
183+
error: (err) => {
184+
console.error('Error while saving settings:', err);
185+
}
186+
})
187+
}
188+
}
189+
155190
exportCSV() {
156191
let progressCSV = '';
157192
this.filteredProgress.forEach((progress) => {

src/app/data/settings.service.ts

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@ import {
1414
extractResponseContent,
1515
GargantuaClientFactory,
1616
} from '../data/gargantua.service';
17+
import { FormControl, FormGroup, Validators } from '@angular/forms';
1718

1819
export interface Settings {
1920
terminal_theme: (typeof themes)[number]['id'];
21+
hide_usernames_status: boolean;
2022
}
2123

2224
/**
@@ -31,11 +33,30 @@ export class SettingsService {
3133
private subject = new Subject<Readonly<Settings>>();
3234
readonly settings$ = concat(this.fetch(), this.subject).pipe(shareReplay(1));
3335

36+
public settingsForm: FormGroup = new FormGroup({
37+
terminal_theme: new FormControl<typeof themes[number]['id'] | null> (null, [Validators.required,]),
38+
hide_usernames_status: new FormControl<boolean>(false),
39+
})
40+
3441
fetch() {
3542
return this.garg.get('/settings').pipe(
3643
map(extractResponseContent),
37-
tap((s: Readonly<Settings>) => {
44+
map((s: Readonly<Settings | null>) =>
45+
s
46+
? s
47+
: ({
48+
terminal_theme: themes[0].id,
49+
hide_usernames_status: false
50+
} as Settings),
51+
),
52+
tap((s: Settings) => {
53+
s.hide_usernames_status = JSON.parse(String(s.hide_usernames_status ?? false));
54+
this.settingsForm.patchValue(s);
3855
this.subject.next(s);
56+
}),
57+
catchError((error) => {
58+
console.error('Error on fetching settings:', error);
59+
return throwError(() => error);
3960
})
4061
);
4162
}
@@ -46,7 +67,10 @@ export class SettingsService {
4667
catchError((e: HttpErrorResponse) => {
4768
return throwError(() => e.error);
4869
}),
49-
tap(() => this.subject.next(newSettings))
70+
tap(() => {
71+
this.settingsForm.patchValue(newSettings);
72+
this.subject.next(newSettings);
73+
})
5074
);
5175
}
5276

@@ -58,4 +82,8 @@ export class SettingsService {
5882
})
5983
);
6084
}
85+
86+
getForm() {
87+
return this.settingsForm;
88+
}
6189
}

src/app/header/header.component.html

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,54 @@
2222
</button>
2323
<clr-dropdown-menu *clrIfOpen clrPosition="bottom-right">
2424
<a (click)="about()" clrDropdownItem>About</a>
25+
<a (click)="openSettings()" clrDropdownItem>Settings</a>
2526
<a (click)="logout()" clrDropdownItem>Logout</a>
2627
</clr-dropdown-menu>
2728
</clr-dropdown>
2829
</div>
2930

31+
<clr-modal #settingsmodal [(clrModalOpen)]="settingsModalOpened">
32+
<h3 class="modal-title">Settings</h3>
33+
<div class="modal-body">
34+
<ng-container *ngIf="fetchingSettings">
35+
<span class="spinner spinner-inline"> Loading... </span>
36+
<span> Loading... </span>
37+
</ng-container>
38+
<ng-container *ngIf="!fetchingSettings">
39+
<form clrForm [formGroup]="settingsForm">
40+
<clr-tabs>
41+
<clr-tab>
42+
<button clrTabLink>General</button>
43+
<clr-tab-content>
44+
<clr-toggle-container>
45+
<label class="clr-col-md-4">Hide Usernames</label>
46+
<clr-toggle-wrapper>
47+
<input
48+
class="clr-col-md-8"
49+
type="checkbox"
50+
clrToggle
51+
name="hide_usernames_status"
52+
formControlName="hide_usernames_status"
53+
/>
54+
<label>Hide Usernames</label>
55+
</clr-toggle-wrapper>
56+
<clr-control-helper
57+
>Keep names hidden in screensharings
58+
</clr-control-helper>
59+
</clr-toggle-container>
60+
</clr-tab-content>
61+
<clr-tab-content> </clr-tab-content>
62+
</clr-tab>
63+
</clr-tabs>
64+
</form>
65+
</ng-container>
66+
</div>
67+
<div class="modal-footer">
68+
<button class="btn" (click)="settingsModalOpened = false">Close</button>
69+
<button class="btn btn-success" (click)="doSaveSettings()" [disabled]="isButtonDisabled">Save</button>
70+
</div>
71+
</clr-modal>
72+
3073
<clr-modal #logoutmodal [(clrModalOpen)]="logoutModalOpened">
3174
<h3 class="modal-title">Confirm Logout</h3>
3275
<div class="modal-body">

src/app/header/header.component.ts

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,28 @@ import { JwtHelperService } from '@auth0/angular-jwt';
88
import { AppConfigService } from '../app-config.service';
99
import { RbacService } from '../data/rbac.service';
1010
import { Title } from '@angular/platform-browser';
11+
import { FormGroup } from '@angular/forms';
12+
import { themes } from '../step/terminal-themes/themes';
13+
import { first } from 'rxjs/operators';
14+
import { SettingsService } from '../data/settings.service';
1115

1216
@Component({
1317
selector: '[app-header]',
1418
templateUrl: './header.component.html',
1519
})
1620
export class HeaderComponent implements OnInit {
1721
public logoutModalOpened: boolean = false;
22+
public settingsModalOpened: boolean = false;
1823
public aboutModalOpened: boolean = false;
1924
public version = environment.version;
2025
public email: string = '';
2126
public configurationRbac: boolean = false;
2227

28+
public fetchingSettings = false;
29+
public settingsForm: FormGroup;
30+
public hide_usernames_status: boolean;
31+
public isButtonDisabled: boolean = false;
32+
2333
private config = this.configService.getConfig();
2434
public title = this.config.title || 'HobbyFarm Administration';
2535
public logo = this.config.logo || '/assets/default/logo.svg';
@@ -29,6 +39,7 @@ export class HeaderComponent implements OnInit {
2939
public helper: JwtHelperService,
3040
public configService: AppConfigService,
3141
private rbacService: RbacService,
42+
private settingsService: SettingsService,
3243
private titleService: Title
3344
) {
3445
this.configService.getLogo(this.logo).then((obj: string) => {
@@ -69,11 +80,13 @@ export class HeaderComponent implements OnInit {
6980
// hence we automatically logout the user
7081
this.doLogout();
7182
}
83+
this.settingsForm = this.settingsService.getForm()
7284
}
7385

86+
@ViewChild('settingsmodal', { static: true }) settingsModal: ClrModal;
7487
@ViewChild('logoutmodal', { static: true }) logoutModal: ClrModal;
7588
@ViewChild('aboutmodal', { static: true }) aboutModal: ClrModal;
76-
89+
7790
public logout() {
7891
this.logoutModal.open();
7992
}
@@ -86,4 +99,40 @@ export class HeaderComponent implements OnInit {
8699
localStorage.removeItem('hobbyfarm_admin_token');
87100
this.router.navigateByUrl('/login');
88101
}
102+
103+
public openSettings() {
104+
this.settingsForm.reset();
105+
this.fetchingSettings = true;
106+
this.settingsService.settings$
107+
.pipe(first())
108+
.subscribe(
109+
({
110+
terminal_theme = 'default',
111+
hide_usernames_status = false,
112+
}) => {
113+
this.settingsForm.setValue({
114+
terminal_theme,
115+
hide_usernames_status
116+
});
117+
118+
this.fetchingSettings = false;
119+
},
120+
);
121+
this.settingsModal.open();
122+
this.hide_usernames_status = this.settingsForm.get('hide_usernames_status')?.value
123+
}
124+
125+
public doSaveSettings() {
126+
this.isButtonDisabled = true;
127+
this.settingsService.update(this.settingsForm.value).subscribe({
128+
next: () => {
129+
this.settingsModalOpened = false;
130+
this.isButtonDisabled = false;
131+
132+
},
133+
error: () => {
134+
setTimeout(() => (this.settingsModalOpened = false), 2000);
135+
},
136+
});
137+
}
89138
}

0 commit comments

Comments
 (0)