Skip to content

Commit 530f8b7

Browse files
authored
Sof fixes (#68)
* Initial epic connection * Handle a couple of errors in smart flow and error display * Temp remove hook * Add auth redirect to separate it from other landing pages * Use dom event to update header and remove problematic layout auth logic * SoF credential display tweak * Fix up login logic and epic himss connection * Site header updates * WIP get summaries to load in screen on login (header state still broken) * Fix header update bug on fresh login
1 parent 20aeb2c commit 530f8b7

File tree

7 files changed

+261
-195
lines changed

7 files changed

+261
-195
lines changed

src/lib/components/layout/Header.svelte

+172-138
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,6 @@
2929
let authService = AuthService.Instance;
3030
3131
let shlStore: Writable<SHLAdminParams[]> = getContext('shlStore');
32-
33-
let shlClient: SHLClient = getContext('shlClient');
3432
3533
let isOpen: Writable<boolean> = getContext('isOpen');
3634
@@ -56,165 +54,201 @@
5654
}
5755
5856
onMount(async () => {
59-
window.onscroll = function() {scrollFunction()};
60-
scrollFunction();
6157
$isOpen = false;
58+
setTimeout(() => {
59+
if (window.innerWidth < 800) {
60+
shrinkNav();
61+
}
62+
}, 10);
6263
6364
window.addEventListener('userFound', handleAuthenticationEvent);
65+
addDynamicNavbarListeners();
6466
});
6567
onDestroy(() => {
6668
window.removeEventListener('userFound', handleAuthenticationEvent);
69+
removeDynamicNavbarListeners();
6770
});
6871
69-
function scrollFunction() {
70-
if (window.scrollY > 40) {
71-
let links = document.getElementsByClassName("nav-link");
72-
if (links.length == 0) return;
73-
for(let i = 0; i < links.length; i++) {
74-
links[i].classList.add("scrolling");
75-
}
76-
document.getElementById("nav-image")?.classList.add("scrolling");
77-
document.getElementsByClassName("navbar")?.[0]?.classList.add("scrolling");
78-
let es = document.getElementsByClassName("nav-text");
79-
if (es.length == 0) return;
80-
for(let i = 0; i < es.length; i++) {
81-
es[i].classList.add("scrolling");
82-
}
83-
} else if (window.scrollY == 0) {
84-
let links = document.getElementsByClassName("nav-link");
85-
if (links.length == 0) return;
86-
for(let i = 0; i < links.length; i++) {
87-
links[i].classList.remove("scrolling");
88-
}
89-
document.getElementById("nav-image")?.classList.remove("scrolling");
90-
document.getElementsByClassName("navbar")?.[0]?.classList.remove("scrolling");
91-
let es = document.getElementsByClassName("nav-text");
92-
if (es.length == 0) return;
93-
for(let i = 0; i < es.length; i++) {
94-
es[i].classList.remove("scrolling");
95-
}
96-
}
97-
}
98-
9972
function closeNav() {
10073
$isOpen = false;
10174
}
10275
103-
let navOpening = false;
104-
document.addEventListener('click', (event) => {
105-
// Ignore clicks on the navbar toggler
106-
if (event.target?.className?.includes('navbar-toggler')) return;
107-
// Ignore clicks on the dropdown toggle menu items
108-
if (event.target?.className?.includes('nav-link') && event.target?.className?.includes('dropdown-toggle')) {
109-
navOpening = true;
110-
setTimeout(() => {
111-
navOpening = false;
112-
}, 100);
113-
return;
76+
function shrinkNav() {
77+
let links = document.getElementsByClassName("nav-link");
78+
if (links.length == 0) return;
79+
for(let i = 0; i < links.length; i++) {
80+
links[i].classList.add("scrolling");
11481
}
115-
closeNav();
116-
});
117-
document.addEventListener('keydown', (event) => {
118-
closeNav();
119-
});
82+
document.getElementById("nav-image")?.classList.add("scrolling");
83+
document.getElementsByClassName("navbar")?.[0]?.classList.add("scrolling");
84+
let es = document.getElementsByClassName("nav-text");
85+
if (es.length == 0) return;
86+
for(let i = 0; i < es.length; i++) {
87+
es[i].classList.add("scrolling");
88+
}
89+
}
12090
121-
window.addEventListener('scroll', (event) => {
122-
if (document.querySelector('.navbar-dropdown.show')?.matches(':hover')) return;
123-
if (document.getElementsByClassName('navbar-collapse collapsing')?.length > 0) return;
124-
if (navOpening) return;
125-
closeNav();
126-
});
91+
function growNav() {
92+
let links = document.getElementsByClassName("nav-link");
93+
if (links.length == 0) return;
94+
for(let i = 0; i < links.length; i++) {
95+
links[i].classList.remove("scrolling");
96+
}
97+
document.getElementById("nav-image")?.classList.remove("scrolling");
98+
document.getElementsByClassName("navbar")?.[0]?.classList.remove("scrolling");
99+
let es = document.getElementsByClassName("nav-text");
100+
if (es.length == 0) return;
101+
for(let i = 0; i < es.length; i++) {
102+
es[i].classList.remove("scrolling");
103+
}
104+
}
105+
function removeDynamicNavbarListeners() {
106+
window.removeEventListener('page-sm', shrinkNav);
107+
window.removeEventListener('page-md', growNav);
108+
}
109+
110+
let navOpening = false;
111+
function addDynamicNavbarListeners() {
112+
window.addEventListener('page-sm', shrinkNav);
113+
window.addEventListener('page-md', growNav);
114+
document.addEventListener('click', (event) => {
115+
// Ignore clicks on the navbar toggler
116+
if (event.target?.className?.includes('navbar-toggler')) return;
117+
// Ignore clicks on the dropdown toggle menu items
118+
if (event.target?.className?.includes('nav-link') && event.target?.className?.includes('dropdown-toggle')) {
119+
navOpening = true;
120+
setTimeout(() => {
121+
navOpening = false;
122+
}, 100);
123+
return;
124+
}
125+
closeNav();
126+
});
127+
document.addEventListener('keydown', (event) => {
128+
closeNav();
129+
});
130+
}
127131
128132
function handleUpdate(event: any) {
129133
$isOpen = event.detail.isOpen;
130134
}
131135
</script>
132-
133-
<Navbar sticky="top"color="light" light expand="md" style="border-bottom: 1px solid rgb(204, 204, 204);">
134-
<NavbarBrand href="https://doh.wa.gov/" target="_blank">
135-
<Row>
136-
<Col>
137-
<Image id="nav-image" alt="Washington State Department of Health Logo" src="/img/doh_logo_doh-black.png"/>
138-
</Col>
139-
</Row>
140-
</NavbarBrand>
141-
<NavbarToggler on:click={() => ($isOpen = !$isOpen)} />
142-
<Collapse class="flex-column" isOpen={$isOpen} navbar expand="md" on:update={handleUpdate}>
143-
<Nav class="ms-auto" navbar>
144-
<LanguageMenu />
145-
</Nav>
146-
<Nav class="ms-auto" navbar>
147-
<NavItem>
148-
<NavLink href={"/"} active={ activeItem === "home" }>Home</NavLink>
149-
</NavItem>
150-
{#if haveUser}
151-
{#await authService.getProfile() then profile}
152-
{#if profile}
153-
<NavItem>
154-
<NavLink href="/home" active={ activeItem === "summaries" }>Summaries</NavLink>
155-
</NavItem>
156-
<NavItem>
157-
<NavLink href="/create" active={ activeItem === "create" }>Create</NavLink>
158-
</NavItem>
159-
<Dropdown nav inNavbar class="navbar-dropdown" size="sm" direction="down">
160-
<DropdownToggle color="primary" nav caret><Icon name="person-circle"/> Account</DropdownToggle>
161-
<DropdownMenu end style="max-height: 500px; overflow:auto">
162-
<DropdownItem header>Welcome, {profile.given_name ?? profile.preferred_username}</DropdownItem>
163-
<DropdownItem
164-
on:click={() => {
165-
authService.logout();
166-
}}><Icon name="box-arrow-right"/> Sign Out</DropdownItem>
167-
<DropdownItem divider />
168-
<DropdownItem header>Demo Options</DropdownItem>
169-
<DropdownItem
170-
on:click={() => {
171-
$mode = ($mode === 'advanced' ? 'normal' : 'advanced');
172-
}}>
173-
<Row class="mr-0" style="min-width:240px">
174-
<Col class="d-flex justify-content-start align-items-center pe-0">
175-
Show Advanced Features
176-
</Col>
177-
<Col class="d-flex justify-content-end ps-0">
178-
{#if $mode == 'advanced'}
179-
<Icon class="text-primary" name="toggle-on"></Icon>
180-
{:else}
181-
<Icon class="text-secondary" name="toggle-off"></Icon>
182-
{/if}
183-
</Col>
184-
</Row>
185-
</DropdownItem>
186-
<DropdownItem divider />
187-
<DropdownItem header>Your Summaries</DropdownItem>
188-
<DropdownItem on:click={() => { goto("/create") }}>
189-
<Icon name="plus-lg" /> Create New Summary
190-
</DropdownItem>
191-
{#if $shlStore.length > 0}
192-
{#each $shlStore as shl, i}
136+
<Row>
137+
<Navbar sticky="top"color="light" light expand="sm" style="border-bottom: 1px solid rgb(204, 204, 204);">
138+
<NavbarBrand href="https://doh.wa.gov/" target="_blank">
139+
<Row>
140+
<Col>
141+
<Image id="nav-image" alt="Washington State Department of Health Logo" src="/img/doh_logo_doh-black.png"/>
142+
</Col>
143+
</Row>
144+
</NavbarBrand>
145+
<NavbarToggler class="me-2" on:click={() => ($isOpen = !$isOpen)} />
146+
<Collapse class="flex-column ms-2" isOpen={$isOpen} navbar expand="sm" on:update={handleUpdate}>
147+
<Nav class="ms-auto" navbar>
148+
<LanguageMenu />
149+
</Nav>
150+
<Nav class="ms-auto" navbar>
151+
<NavItem>
152+
<NavLink href={"/"} active={ activeItem === "home" }>Home</NavLink>
153+
</NavItem>
154+
{#if haveUser}
155+
{#await authService.getProfile() then profile}
156+
{#if profile}
157+
<NavItem>
158+
<NavLink href="/home" active={ activeItem === "summaries" }>Summaries</NavLink>
159+
</NavItem>
160+
<NavItem>
161+
<NavLink href="/create" active={ activeItem === "create" }>Create</NavLink>
162+
</NavItem>
163+
<Dropdown nav inNavbar class="navbar-dropdown" size="sm" direction="down">
164+
<DropdownToggle color="primary" nav caret><Icon name="person-circle"/> Account</DropdownToggle>
165+
<DropdownMenu end style="max-height: 500px; overflow:auto">
166+
<DropdownItem header>Welcome, {profile.given_name ?? profile.preferred_username}</DropdownItem>
167+
<DropdownItem
168+
on:click={() => {
169+
authService.logout();
170+
}}><Icon name="box-arrow-right"/> Sign Out</DropdownItem>
171+
<DropdownItem divider />
172+
<DropdownItem header>Demo Options</DropdownItem>
193173
<DropdownItem
194174
on:click={() => {
195-
goto('/view/' + shl.id);
196-
}}>{shl.label || `Summary ${i + 1}`}</DropdownItem>
197-
{/each}
198-
{/if}
199-
</DropdownMenu>
200-
</Dropdown>
175+
$mode = ($mode === 'advanced' ? 'normal' : 'advanced');
176+
}}>
177+
<Row class="mr-0" style="min-width:240px">
178+
<Col class="d-flex justify-content-start align-items-center pe-0">
179+
Show Advanced Features
180+
</Col>
181+
<Col class="d-flex justify-content-end ps-0">
182+
{#if $mode == 'advanced'}
183+
<Icon class="text-primary" name="toggle-on"></Icon>
184+
{:else}
185+
<Icon class="text-secondary" name="toggle-off"></Icon>
186+
{/if}
187+
</Col>
188+
</Row>
189+
</DropdownItem>
190+
<DropdownItem divider />
191+
<DropdownItem header>Your Summaries</DropdownItem>
192+
<DropdownItem on:click={() => { goto("/create") }}>
193+
<Icon name="plus-lg" /> Create New Summary
194+
</DropdownItem>
195+
{#if $shlStore.length > 0}
196+
{#each $shlStore as shl, i}
197+
<DropdownItem
198+
on:click={() => {
199+
goto('/view/' + shl.id);
200+
}}>{shl.label || `Summary ${i + 1}`}</DropdownItem>
201+
{/each}
202+
{/if}
203+
</DropdownMenu>
204+
</Dropdown>
205+
{:else}
206+
<NavItem>
207+
<NavLink on:click={() => authService.login()}><Icon name="person-circle"/> Sign In</NavLink>
208+
</NavItem>
209+
{/if}
210+
{/await}
201211
{:else}
202-
<NavItem>
203-
<NavLink on:click={() => authService.login()}><Icon name="person-circle"/> Sign In</NavLink>
204-
</NavItem>
205-
{/if}
206-
{/await}
207-
{:else}
208-
<NavItem>
209-
<NavLink on:click={() => authService.login()}><Icon name="person-circle"/> Sign In</NavLink>
210-
</NavItem>
211-
{/if}
212-
</Nav>
213-
</Collapse>
214-
</Navbar>
215-
<Banner title="International Patient Summary Prototype"/>
216-
212+
<NavItem>
213+
<NavLink on:click={() => authService.login()}><Icon name="person-circle"/> Sign In</NavLink>
214+
</NavItem>
215+
{/if}
216+
</Nav>
217+
</Collapse>
218+
</Navbar>
219+
<Banner title="International Patient Summary Prototype"/>
220+
</Row>
217221
<style>
222+
:global(#nav-image) {
223+
width: 240px;
224+
-webkit-transition: all 0.06s linear;
225+
-moz-transition: all 0.06s linear;
226+
-o-transition: all 0.06s linear;
227+
transition: all 0.06s linear;
228+
}
229+
:global(.nav-text) {
230+
font-size:medium;
231+
-webkit-transition: all 0.06s linear;
232+
-moz-transition: all 0.06s linear;
233+
-o-transition: all 0.06s linear;
234+
transition: all 0.06s linear;
235+
}
236+
:global(.nav-link.scrolling) {
237+
padding-top: 0rem !important;
238+
padding-bottom: 0.25rem !important;
239+
}
240+
:global(#nav-image.scrolling) {
241+
width: 160px !important;
242+
margin-left: 10px;
243+
}
244+
:global(.nav-text.scrolling) {
245+
font-size: xx-small;
246+
color: #000; /* Fallback for older browsers */
247+
color: rgba(0, 0, 0, 0.0);
248+
}
249+
:global(.navbar.scrolling) {
250+
padding: 0px !important;
251+
}
218252
:global(.nav-link) {
219253
position: relative;
220254
}

src/lib/config.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export const SOF_HOSTS = [
3030
{
3131
id: "epic-himss",
3232
name: "Epic - HIMSS Demo",
33-
url: " https://ihe-nimbus.epic.com/Interconnect-FHIR/api/FHIR/R4",
33+
url: "https://ihe-nimbus.epic.com/Interconnect-FHIR/api/FHIR/R4",
3434
clientId: import.meta.env.VITE_EPIC_CLIENT_ID,
3535
note: "user / pass"
3636
},
@@ -39,14 +39,14 @@ export const SOF_HOSTS = [
3939
name: "Epic Demo",
4040
url: "https://fhir.epic.com/interconnect-fhir-oauth/api/FHIR/R4",
4141
clientId: import.meta.env.VITE_EPIC_CLIENT_ID,
42-
note: "fhircamila / epicepic1 <br> <a style='color: grey; font-size: small' href='https://fhir.epic.com/Documentation?docId=testpatients' target='_blank' rel='noreferrer'>More credentials <span style='vertical-align: text-top; font-size: x-small' class='bi-box-arrow-up-right' /></a>"
42+
note: "fhircamila / epicepic1 <br> <a style='color: grey; font-size: small' href='https://fhir.epic.com/Documentation?docId=testpatients' target='_blank' rel='noreferrer'>More credentials <span style='vertical-align: text-bottom; font-size: x-small' class='bi-chevron-double-right' /></a>"
4343
},
4444
{
4545
id: "cerner",
4646
name: "Oracle Cerner Demo",
4747
url: "https://fhir-myrecord.cerner.com/r4/ec2458f2-1e24-41c8-b71b-0e701af7583d",
4848
clientId: import.meta.env.VITE_CERNER_CLIENT_ID,
49-
note: "nancysmart / Cerner01 <br> <a style='color: grey; font-size: small' href='https://docs.google.com/document/u/1/d/e/2PACX-1vQwyX3px4qi5t1O6_El6022zYt4ymKAWCrcgxcX5NvYGUJAkJ4WFwOnLoikow6rEccpFZzDWBdcBqsQ/pub' target='_blank' rel='noreferrer'>More credentials <span style='vertical-align: text-top; font-size: x-small' class='bi-box-arrow-up-right' /></a>"
49+
note: "nancysmart / Cerner01 <br> <a style='color: grey; font-size: small' href='https://docs.google.com/document/u/1/d/e/2PACX-1vQwyX3px4qi5t1O6_El6022zYt4ymKAWCrcgxcX5NvYGUJAkJ4WFwOnLoikow6rEccpFZzDWBdcBqsQ/pub' target='_blank' rel='noreferrer'>More credentials <span style='vertical-align: text-bottom; font-size: x-small' class='bi-chevron-double-right' /></a>"
5050
},
5151
{
5252
id: "smit",
@@ -168,8 +168,8 @@ export const EXAMPLE_IPS = {
168168
'Peter Kieth Jordan': 'https://raw.githubusercontent.com/jddamore/IPSviewer/4eedba9df34afbf3eb20d98c49d36afc7f9ce104/samples/connectathon_Jan2025/new_IPS_Example.json',
169169
'Johanna Petronella Maria (Jo) van Putten-van der Giessen': "https://raw.githubusercontent.com/jddamore/IPSviewer/4eedba9df34afbf3eb20d98c49d36afc7f9ce104/samples/connectathon_archive/NL_core_patient_01.json",
170170
'Martha Mum': 'https://hl7-ips-server.hl7.org/fhir/Patient/15/$summary',
171-
'Meditech 1': 'https://dev-mtx-interop.meditech.com:443/v2/ips/STU1/Patient/f3b430be-1f8a-53d3-8261-4ffbafa05a61/$summary',
172-
// 'Meditech 2': 'https://dev-mtx-interop.meditech.com:443/v2/ips/STU1/Patient/9bad7dc5-47ad-5022-82e7-0cb0aab13ee9/$summary', // Error returned
171+
'MEDITECH 1': 'https://dev-mtx-interop.meditech.com:443/v2/ips/STU1/Patient/f3b430be-1f8a-53d3-8261-4ffbafa05a61/$summary',
172+
// 'MEDITECH 2': 'https://dev-mtx-interop.meditech.com:443/v2/ips/STU1/Patient/9bad7dc5-47ad-5022-82e7-0cb0aab13ee9/$summary', // Error returned
173173
'Angela Roster': 'https://fhir.ips-demo.dev.cirg.uw.edu/fhir/Patient/10965/$summary',
174174
'Horace Skelly': 'https://fhir.ips-demo.dev.cirg.uw.edu/fhir/Patient/11142/$summary',
175175
'Anonymous': 'https://fhir.ips-demo.dev.cirg.uw.edu/fhir/Patient/10999/$summary',

src/lib/utils/AuthService.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,12 @@ export class AuthService {
7474
}
7575

7676
login(): Promise<void> {
77-
let currentUrl = window.location.href.split('?')[0];
78-
return this.userManager.signinRedirect({ url_state: currentUrl });
77+
let currentUrl = new URL(window.location.href.split('?')[0]);
78+
let redirectUrl = currentUrl.href;
79+
if (currentUrl.pathname === '/') {
80+
redirectUrl = '/home';
81+
}
82+
return this.userManager.signinRedirect({ url_state: redirectUrl });
7983
}
8084

8185
renewToken(): Promise<User | null> {

0 commit comments

Comments
 (0)