Skip to content

Commit 06c72cf

Browse files
fix(terms): resolve markdown rendering and Proceed button race condition (#6)
* fix(terms): resolve markdown rendering and Proceed button race condition * fix(ui): avoid double proceed click on terms acceptance * feat: Expect the MIP version from env variables. * feat: centralize runtime environment variable access, including version information, into a new `RuntimeEnvService`
1 parent d9d6244 commit 06c72cf

12 files changed

Lines changed: 128 additions & 36 deletions

File tree

Dockerfile

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,9 @@ ENV PLATFORM_BACKEND_SERVER=platform-backend-service:8080 \
3535
NOTEBOOK_ENABLED=0 \
3636
JUPYTER_SERVER=jupyterhub:8000 \
3737
JUPYTER_CONTEXT=notebook \
38-
JUPYTER_LANDING_PATH=/hub/spawn \
39-
FRONTEND_VERSION=10.0.1 \
40-
BACKEND_VERSION=8.2.0 \
41-
EXAFLOW_VERSION=0.28.0
38+
JUPYTER_LANDING_PATH=/hub/spawn
4239
COPY nginx.conf.template /etc/nginx/templates/default.conf.template
4340
RUN rm -rf /usr/share/nginx/html/*
4441
COPY --from=build /app/dist/fl-platform/browser /usr/share/nginx/html
4542
EXPOSE 80
46-
CMD ["/bin/sh", "-c", "envsubst '$$PLATFORM_BACKEND_SERVER $$PLATFORM_BACKEND_CONTEXT $$NOTEBOOK_ENABLED $$JUPYTER_SERVER $$JUPYTER_CONTEXT' < /etc/nginx/templates/default.conf.template > /etc/nginx/conf.d/default.conf && envsubst '$$FRONTEND_VERSION $$BACKEND_VERSION $$EXAFLOW_VERSION $$NOTEBOOK_ENABLED $$JUPYTER_CONTEXT $$JUPYTER_LANDING_PATH' < /usr/share/nginx/html/assets/env.template.js > /usr/share/nginx/html/assets/env.js && exec nginx -g 'daemon off;'"]
43+
CMD ["/bin/sh", "-c", "envsubst '$$PLATFORM_BACKEND_SERVER $$PLATFORM_BACKEND_CONTEXT $$NOTEBOOK_ENABLED $$JUPYTER_SERVER $$JUPYTER_CONTEXT' < /etc/nginx/templates/default.conf.template > /etc/nginx/conf.d/default.conf && envsubst '$$FRONTEND_VERSION $$BACKEND_VERSION $$EXAFLOW_VERSION $$MIP_VERSION $$NOTEBOOK_ENABLED $$JUPYTER_CONTEXT $$JUPYTER_LANDING_PATH' < /usr/share/nginx/html/assets/env.template.js > /usr/share/nginx/html/assets/env.js && exec nginx -g 'daemon off;'"]

src/app/guards/terms.guard.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { inject, Injectable } from '@angular/core';
22
import { CanActivate, Router, RouterStateSnapshot, ActivatedRouteSnapshot } from '@angular/router';
3-
import { Observable } from 'rxjs';
3+
import { Observable, of } from 'rxjs';
44
import { map, take } from 'rxjs/operators';
55
import { AuthService } from '../services/auth.service';
66
import { TermsService } from '../services/terms.service';
@@ -14,6 +14,11 @@ export class TermsGuard implements CanActivate {
1414
constructor() { }
1515

1616
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
17+
const currentAuthState = this.authService.authState();
18+
if (currentAuthState.status === 'authenticated' && this.termsService.hasAgreed(currentAuthState.user)) {
19+
return of(true);
20+
}
21+
1722
return this.authService.onAuthResolved().pipe(
1823
take(1),
1924
map((authState) => {

src/app/pages/experiment-studio/algorithm-panel/algorithm-panel.component.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,10 +295,12 @@ <h5>{{ resultDisplayTitle() }}</h5>
295295
[enumMaps]="enumMaps()" [yVar]="yVar()" [xVar]="xVar()" [labelMap]="labelMap()"
296296
[algorithmLabel]="resultAlgorithmLabel()" />
297297

298+
@if (mipVersion) {
298299
<div class="mip-version-bar">
299300
<span class="mip-version-label">MIP Version:</span>
300301
<span class="mip-version-value">{{ mipVersion }}</span>
301302
</div>
303+
}
302304
</section>
303305
} @else if (selectedAlgorithm()) {
304306
<section class="experiment-settings">

src/app/pages/experiment-studio/algorithm-panel/algorithm-panel.component.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { ErrorService } from '../../../services/error.service';
1313
import { AuthService } from '../../../services/auth.service';
1414
import { SpinnerComponent } from '../../shared/spinner/spinner.component';
1515
import { VariableTypes } from '../../../core/constants/algorithm.constants';
16+
import { RuntimeEnvService } from '../../../services/runtime-env.service';
1617

1718

1819
@Component({
@@ -34,6 +35,7 @@ export class AlgorithmPanelComponent {
3435
pdfExport = inject(ResultsPdfExportService);
3536
private errorService = inject(ErrorService);
3637
private authService = inject(AuthService);
38+
private runtimeEnvService = inject(RuntimeEnvService);
3739
Object = Object;
3840
experimentStudioService = inject(ExperimentStudioService);
3941
sessionStorage = inject(SessionStorageService);
@@ -48,7 +50,7 @@ export class AlgorithmPanelComponent {
4850
saveAsName = signal('');
4951
loadingText = signal('Processing experiment...');
5052
showSuccessNotification = signal(false);
51-
mipVersion = (window as any).__env?.MIP_VERSION || '9.0.0';
53+
readonly mipVersion = this.runtimeEnvService.mipVersion;
5254

5355
readonly selectedAlgorithm = this.experimentStudioService.selectedAlgorithm;
5456
readonly enumMaps = computed(() => this.experimentStudioService.getCategoricalEnumMaps());

src/app/pages/experiment-studio/statistic-analysis-panel/statistic-analysis-panel.component.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { EChartsOption } from 'echarts';
1717
import { PdfExportService } from '../../../services/pdf-export.service';
1818
import { SpinnerComponent } from '../../shared/spinner/spinner.component';
1919
import { buildGroupedBarChart } from '../visualisations/charts/renderers/grouped-bar-chart';
20+
import { RuntimeEnvService } from '../../../services/runtime-env.service';
2021

2122

2223
type TabKey = 'Variables' | 'Model' | 'Distributions';
@@ -45,12 +46,13 @@ export class StatisticAnalysisPanelComponent implements OnChanges {
4546
@ViewChildren(ChartRendererComponent)
4647
chartRenderers!: QueryList<ChartRendererComponent>;
4748
isExporting = false;
48-
readonly mipVersion = (window as any).__env?.MIP_VERSION || '9.0.0';
4949

5050
private expStudioService = inject(ExperimentStudioService);
5151
private chartBuilder = inject(ChartBuilderService);
5252
private pdfExportService = inject(PdfExportService);
5353
private cdr = inject(ChangeDetectorRef);
54+
private runtimeEnvService = inject(RuntimeEnvService);
55+
readonly mipVersion = this.runtimeEnvService.mipVersion;
5456

5557
openAccordions: Record<string, boolean> = {};
5658
isLoading = true;

src/app/pages/experiments-dashboard/experiment-detail/experiment-detail.component.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ export class ExperimentDetailsComponent {
5656
readonly nameDraft = signal<string>('');
5757
readonly nameSaving = signal<boolean>(false);
5858
readonly nameError = signal<string | null>(null);
59-
readonly mipVersion = (window as any).__env?.MIP_VERSION || '9.0.0';
6059

6160
private codeToLabelSignal = signal<Record<string, string>>({});
6261
private enumMapsSignal = signal<EnumMaps>({});
@@ -290,7 +289,7 @@ export class ExperimentDetailsComponent {
290289
covariates: this.covariatesWithLabels().map((c) => c.label),
291290
filters: this.filtersWithLabels().map((f) => f.label),
292291
transformations,
293-
mipVersion: this.mipVersion,
292+
mipVersion: this.selectedExperiment()?.mipVersion ?? fullExperiment?.mipVersion ?? null,
294293
},
295294
algorithmKey: fullExperiment?.algorithm?.name ?? this.experimentalAlgorithmName(),
296295
result,

src/app/pages/shared/footer/footer.component.html

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,13 @@
2929
</div>
3030

3131
<div class="footer-right">
32+
@if (versionEntries.length) {
3233
<div class="footer-versions">
33-
<span>Frontend: {{ versions.frontend }}, </span>
34-
<span>Backend: {{ versions.backend }}, </span>
35-
<span>Exaflow: {{ versions.exaflow }}, </span>
36-
<span>MIP: {{ versions.mip }}</span>
34+
@for (version of versionEntries; track version.label) {
35+
<span>{{ version.label }}: {{ version.value }}</span>
36+
}
3737
</div>
38+
}
3839
</div>
3940
</div>
40-
</footer>
41+
</footer>
Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { CommonModule } from '@angular/common';
2-
import { ChangeDetectionStrategy, Component } from '@angular/core';
2+
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
33
import { RouterModule } from '@angular/router';
4+
import { RuntimeEnvService } from '../../../services/runtime-env.service';
45

56
@Component({
67
selector: 'app-footer',
@@ -10,11 +11,8 @@ import { RouterModule } from '@angular/router';
1011
changeDetection: ChangeDetectionStrategy.OnPush,
1112
})
1213
export class FooterComponent {
13-
currentYear = new Date().getFullYear();
14-
versions = {
15-
frontend: (window as any).__env?.FRONTEND_VERSION || '1.0.0',
16-
backend: (window as any).__env?.BACKEND_VERSION || '9.0.0',
17-
exaflow: (window as any).__env?.EXAFLOW_VERSION || '1.0.0',
18-
mip: (window as any).__env?.MIP_VERSION || '9.0.0'
19-
};
14+
private readonly runtimeEnvService = inject(RuntimeEnvService);
15+
16+
readonly currentYear = new Date().getFullYear();
17+
readonly versionEntries = this.runtimeEnvService.versionEntries;
2018
}

src/app/pages/terms-page/terms-page.component.ts

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -74,16 +74,8 @@ export class TermsPageComponent implements OnInit {
7474

7575
this.http.post('/services/activeUser/agreeNDA', {}).subscribe({
7676
next: () => {
77-
this.authService.refreshAuthState().subscribe({
78-
next: () => {
79-
navigateToDashboard();
80-
},
81-
error: () => {
82-
this.submitting = false;
83-
this.acceptError = true;
84-
this.cdr.markForCheck();
85-
}
86-
});
77+
this.authService.markTermsAccepted();
78+
navigateToDashboard();
8779
},
8880
error: () => {
8981
this.submitting = false;
@@ -251,12 +243,14 @@ export class TermsPageComponent implements OnInit {
251243
.replace(/\[([^\]]+)\]\((https?:\/\/[^)]+)\)/g, '<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>')
252244
.replace(/\[([^\]]+)\]\((mailto:[^)]+)\)/g, '<a href="$2">$1</a>')
253245
.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>')
254-
.replace(/_([^_]+)_/g, '<em>$1</em>');
246+
.replace(/_([^_]+)_/g, '<em>$1</em>')
247+
.replace(/&lt;u&gt;/gi, '<u>')
248+
.replace(/&lt;\/u&gt;/gi, '</u>');
255249
}
256250

257251
private escapeHtml(value: string): string {
258252
return value
259-
.replace(/&/g, '&amp;')
253+
.replace(/&(?!#?[a-zA-Z0-9]+;)/g, '&amp;')
260254
.replace(/</g, '&lt;')
261255
.replace(/>/g, '&gt;')
262256
.replace(/"/g, '&quot;')

src/app/services/auth.service.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,16 @@ export class AuthService {
8989
return this.authState().user ?? null;
9090
}
9191

92+
markTermsAccepted(): void {
93+
const state = this.authState();
94+
if (state.status === 'authenticated' && state.user) {
95+
this.authStateSignal.set({
96+
...state,
97+
user: { ...state.user, agreeNDA: true }
98+
});
99+
}
100+
}
101+
92102
onAuthResolved(): Observable<AuthState> {
93103
return this.authState$.pipe(filter((state) => state.status !== 'checking'));
94104
}

0 commit comments

Comments
 (0)