Skip to content

Commit 8f0cc3b

Browse files
committed
adding license agreement modal
1 parent 8c1f5ef commit 8f0cc3b

7 files changed

Lines changed: 269 additions & 5 deletions

File tree

src/app/app.component.ts

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import { Router, NavigationEnd, ActivatedRoute } from '@angular/router';
66
import { MenuService } from './services/menu.service';
77
import { MatDialog } from '@angular/material/dialog';
88
import { LanguageConfigComponent } from './util/language-config/language-config.component';
9+
import { LicenseAgreementComponent } from './license-agreement/license-agreement.component';
10+
import { CookieService } from './services/cookie.service';
911
import { catchError, of, skip, Subject, switchMap, tap } from 'rxjs';
1012
import { HttpClient } from '@angular/common/http';
1113

@@ -55,7 +57,8 @@ export class AppComponent {
5557
private dialog: MatDialog,
5658
private cdRef: ChangeDetectorRef,
5759
private http: HttpClient,
58-
private activatedRoute: ActivatedRoute) {
60+
private activatedRoute: ActivatedRoute,
61+
private cookieService: CookieService) {
5962
this.router.events.subscribe(event => {
6063
if (event instanceof NavigationEnd) {
6164
// Send pageview event to Google Analytics on each route change.
@@ -67,6 +70,21 @@ export class AppComponent {
6770
}
6871

6972
ngOnInit(): void {
73+
// Check embedded mode immediately from snapshot
74+
const params = this.activatedRoute.snapshot.queryParams;
75+
this.embeddedMode = params['embedded'] === 'true';
76+
77+
// Wait a moment before checking license to ensure everything is initialized
78+
setTimeout(() => {
79+
// If in embedded mode, automatically accept license (no modal)
80+
if (this.embeddedMode) {
81+
this.cookieService.setCookie('snomedLicenseAccepted', 'true', 180);
82+
} else {
83+
// Check license agreement for normal mode
84+
this.checkLicenseAgreement();
85+
}
86+
}, 500);
87+
7088
this.demos = this.menuService.getDemos();
7189
this.bindingsForExport = [];
7290
let spec: any[] = this.codingSpecService.getCodingSpec();
@@ -134,7 +152,7 @@ export class AppComponent {
134152
}
135153
if (params['edition']) {
136154
this.updateCodeSystemOptions(params['edition']);
137-
}
155+
}
138156
});
139157

140158
this.terminologyService.lang$.subscribe(lang => {
@@ -353,7 +371,33 @@ export class AppComponent {
353371
});
354372

355373
dialogRef.afterClosed().subscribe((result) => {
356-
console.log('Dialog was closed. Result:', result);
374+
// Dialog closed
375+
});
376+
}
377+
378+
checkLicenseAgreement(): void {
379+
const licenseAccepted = this.cookieService.getCookie('snomedLicenseAccepted');
380+
381+
if (!licenseAccepted) {
382+
this.openLicenseDialog();
383+
}
384+
}
385+
386+
openLicenseDialog(): void {
387+
const dialogRef = this.dialog.open(LicenseAgreementComponent, {
388+
width: '900px',
389+
disableClose: true,
390+
panelClass: 'license-dialog-container'
391+
});
392+
393+
dialogRef.afterClosed().subscribe((result) => {
394+
if (result === true) {
395+
// User accepted the license - set cookie for 6 months (180 days)
396+
this.cookieService.setCookie('snomedLicenseAccepted', 'true', 180);
397+
} else {
398+
// User declined - redirect to SNOMED International website
399+
window.location.href = 'https://www.snomed.org';
400+
}
357401
});
358402
}
359403

src/app/app.module.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ import { InteroperabilityComponent } from './benefits-demo/interoperability/inte
115115
import { AiAssistedEntryComponent } from './benefits-demo/ai-assisted-entry/ai-assisted-entry.component';
116116
import { ExpoQuiz2025Component } from './expo-quiz-2025/expo-quiz-2025.component';
117117
import { BatchPatientDialogComponent } from './benefits-demo/batch-patient-dialog/batch-patient-dialog.component';
118+
import { LicenseAgreementComponent } from './license-agreement/license-agreement.component';
118119

119120
const app = initializeApp(firebaseConfig);
120121

@@ -189,7 +190,8 @@ const app = initializeApp(firebaseConfig);
189190
InteroperabilityComponent,
190191
AiAssistedEntryComponent,
191192
ExpoQuiz2025Component,
192-
BatchPatientDialogComponent
193+
BatchPatientDialogComponent,
194+
LicenseAgreementComponent
193195
],
194196
bootstrap: [AppComponent],
195197
schemas: [CUSTOM_ELEMENTS_SCHEMA], imports: [BrowserModule,
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
.license-content {
2+
max-width: 800px;
3+
}
4+
5+
.license-text {
6+
height: 350px;
7+
overflow: auto;
8+
margin: 20px 0;
9+
border: 1px solid #ddd;
10+
padding: 20px;
11+
background-color: #f9f9f9;
12+
}
13+
14+
.license-text p {
15+
margin-bottom: 15px;
16+
line-height: 1.6;
17+
}
18+
19+
.license-text ol {
20+
margin-left: 20px;
21+
margin-bottom: 15px;
22+
}
23+
24+
.license-text ol > li {
25+
margin-bottom: 10px;
26+
line-height: 1.6;
27+
}
28+
29+
.license-text ol ol {
30+
margin-left: 30px;
31+
margin-top: 10px;
32+
}
33+
34+
.license-text a {
35+
color: #1976d2;
36+
text-decoration: none;
37+
}
38+
39+
.license-text a:hover {
40+
text-decoration: underline;
41+
}
42+
43+
/* Scrollbar styling */
44+
.license-text::-webkit-scrollbar {
45+
width: 10px;
46+
}
47+
48+
.license-text::-webkit-scrollbar-track {
49+
background: #f1f1f1;
50+
border-radius: 5px;
51+
}
52+
53+
.license-text::-webkit-scrollbar-thumb {
54+
background: #888;
55+
border-radius: 5px;
56+
}
57+
58+
.license-text::-webkit-scrollbar-thumb:hover {
59+
background: #555;
60+
}
61+
62+
/* Dialog actions */
63+
mat-dialog-actions {
64+
padding: 16px 24px;
65+
margin: 0;
66+
min-height: auto;
67+
}
68+
69+
mat-dialog-actions button {
70+
margin-left: 8px;
71+
}
72+
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<h2 mat-dialog-title>SNOMED CT Implementation Demonstrator License Agreement</h2>
2+
3+
<div mat-dialog-content class="license-content">
4+
<p>In order to use the Implementation Demonstrator, please accept the following license agreement:</p>
5+
6+
<div class="license-text">
7+
<p><strong>Implementation Demonstrator</strong> includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of SNOMED International. All rights reserved. SNOMED CT® was originally created by the College of American Pathologists.</p>
8+
9+
<p>"SNOMED", "SNOMED CT" and "SNOMED Clinical Terms" are registered trademarks of the SNOMED International (<a href="http://www.snomed.org" target="_blank">www.snomed.org</a>).</p>
10+
11+
<p>Use of SNOMED CT in <strong>Implementation Demonstrator</strong> is governed by the conditions of the following SNOMED CT license issued by the SNOMED International:</p>
12+
13+
<ol>
14+
<li>The meaning of the terms "Affiliate", or "Data Analysis System", "Data Creation System", "Derivative", "End User", "Extension", "Member", "Non-Member Territory", "SNOMED CT" and "SNOMED CT Content" are as defined in the SNOMED International Affiliate License Agreement (see <a href="www.snomed.org/license.pdf" target="_blank">www.snomed.org/license.pdf</a>).</li>
15+
16+
<li>Information about Affiliate Licensing is available at <a href="http://snomed.org/license-affiliate" target="_blank">http://snomed.org/license-affiliate</a>. Individuals or organizations wishing to register as SNOMED International Affiliates can register at <a href="https://mlds.ihtsdotools.org" target="_blank">mlds.ihtsdotools.org</a>, subject to acceptance of the Affiliate License Agreement (see <a href="http://snomed.org/license-affiliate" target="_blank">http://snomed.org/license-affiliate</a>).</li>
17+
18+
<li>The current list of SNOMED International Member Territories can be viewed at <a href="http://snomed.org/members" target="_blank">snomed.org/members</a>. Countries not included in that list are "Non-Member Territories".</li>
19+
20+
<li>
21+
End Users, that do not hold an SNOMED International Affiliate License, may access SNOMED CT® using <strong>Implementation Demonstrator</strong> subject to acceptance of and adherence to the following sub-license limitations:
22+
<ol type="a">
23+
<li>The sub-licensee is only permitted to access SNOMED CT® using this software (or service) for the purpose of exploring and evaluating the terminology.</li>
24+
<li>The sub-licensee is not permitted the use of this software as part of a system that constitutes a SNOMED CT "Data Creation System" or "Data Analysis System", as defined in the SNOMED International Affiliate License. This means that the sub-licensee must not use <strong>Implementation Demonstrator</strong> to add or copy SNOMED CT identifiers into any type of record system, database or document.</li>
25+
<li>The sub-licensee is not permitted to translate or modify SNOMED CT Content or Derivatives.</li>
26+
<li>The sub-licensee is not permitted to distribute or share SNOMED CT Content or Derivatives.</li>
27+
</ol>
28+
</li>
29+
30+
<li>
31+
SNOMED International Affiliates may use <strong>Implementation Demonstrator</strong> as part of a "Data Creation System" or "Data Analysis System" subject to the following conditions:
32+
<ol type="a">
33+
<li>The SNOMED International Affiliate, using <strong>Implementation Demonstrator</strong> must accept full responsibility for any reporting and fees due for use or deployment of such a system in a Non-Member Territory.</li>
34+
<li>The SNOMED International Affiliate must not use <strong>Implementation Demonstrator</strong> to access or interact with SNOMED CT in any way that is not permitted by the Affiliate License Agreement.</li>
35+
<li>In the event of termination of the Affiliate License Agreement, the use of <strong>Implementation Demonstrator</strong> will be subject to the End User limitations noted in 4.</li>
36+
</ol>
37+
</li>
38+
</ol>
39+
</div>
40+
</div>
41+
42+
<div mat-dialog-actions align="end">
43+
<button mat-button (click)="onDecline()">Decline</button>
44+
<button mat-raised-button color="primary" (click)="onAccept()">Accept</button>
45+
</div>
46+
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { Component } from '@angular/core';
2+
import { MatDialogRef } from '@angular/material/dialog';
3+
4+
@Component({
5+
selector: 'app-license-agreement',
6+
templateUrl: './license-agreement.component.html',
7+
styleUrls: ['./license-agreement.component.css'],
8+
standalone: false
9+
})
10+
export class LicenseAgreementComponent {
11+
12+
constructor(
13+
public dialogRef: MatDialogRef<LicenseAgreementComponent>
14+
) {}
15+
16+
onAccept(): void {
17+
this.dialogRef.close(true);
18+
}
19+
20+
onDecline(): void {
21+
this.dialogRef.close(false);
22+
}
23+
}
24+

src/app/services/cookie.service.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { Injectable } from '@angular/core';
2+
3+
@Injectable({
4+
providedIn: 'root'
5+
})
6+
export class CookieService {
7+
8+
/**
9+
* Set a cookie with a given name, value, and expiration days
10+
*/
11+
setCookie(name: string, value: string, days: number): void {
12+
const date = new Date();
13+
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
14+
const expires = `expires=${date.toUTCString()}`;
15+
document.cookie = `${name}=${value};${expires};path=/`;
16+
}
17+
18+
/**
19+
* Get a cookie value by name
20+
*/
21+
getCookie(name: string): string | null {
22+
const nameEQ = `${name}=`;
23+
const cookies = document.cookie.split(';');
24+
25+
for (let cookie of cookies) {
26+
cookie = cookie.trim();
27+
if (cookie.indexOf(nameEQ) === 0) {
28+
return cookie.substring(nameEQ.length);
29+
}
30+
}
31+
return null;
32+
}
33+
34+
/**
35+
* Delete a cookie by name
36+
*/
37+
deleteCookie(name: string): void {
38+
document.cookie = `${name}=;expires=Thu, 01 Jan 1970 00:00:00 UTC;path=/;`;
39+
}
40+
41+
/**
42+
* Check if a cookie exists
43+
*/
44+
hasCookie(name: string): boolean {
45+
return this.getCookie(name) !== null;
46+
}
47+
}
48+

src/styles.css

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,32 @@ body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }
3333
@apply text-blue-500;
3434
@apply hover:text-blue-700;
3535
}
36-
}
36+
}
37+
38+
/* Material Snackbar styles */
39+
::ng-deep .warning-snackbar {
40+
background-color: #ff9800 !important;
41+
color: white !important;
42+
}
43+
44+
::ng-deep .warning-snackbar .mat-simple-snackbar-action {
45+
color: white !important;
46+
}
47+
48+
::ng-deep .error-snackbar {
49+
background-color: #f44336 !important;
50+
color: white !important;
51+
}
52+
53+
::ng-deep .error-snackbar .mat-simple-snackbar-action {
54+
color: white !important;
55+
}
56+
57+
::ng-deep .green-snackbar {
58+
background-color: #4caf50 !important;
59+
color: white !important;
60+
}
61+
62+
::ng-deep .green-snackbar .mat-simple-snackbar-action {
63+
color: white !important;
64+
}

0 commit comments

Comments
 (0)