nuVistA/htdocs/RoutePatientConsults.vue
2023-08-09 23:16:25 -04:00

196 lines
5.6 KiB
Vue

<template>
<Subtitle value="Consults" />
<Subtitle :value="patient_info.name" />
<div class="row">
<div class="selector col-12" :class="{ 'col-xl-4': selection_text }">
<div class="card mb-3 shadow">
<div class="card-header">{{resultset.length > 0 ? resultset.length : 'No'}} record{{resultset.length == 1 ? '' : 's'}}</div>
<ul class="scroller list-group list-group-flush" :class="{ 'list-skinny': selection_text }" ref="scroller">
<router-link v-for="item in resultset" :to="'/patient/' + patient_dfn + '/consult/' + item.IEN" replace custom v-slot="{ navigate, href }">
<li :key="item" class="record list-group-item" :class="{ 'active': selection == item.IEN }" :title="datetimestring(strptime_vista(item.time)) + ' #' + item.IEN + '\n(' + item.status + ') ' + item.text" @click="navigate">
<div class="row">
<div class="cell col-4"><router-link :to="href" replace>{{datestring(strptime_vista(item.time))}}</router-link></div>
<div class="cell col-8">
<template v-if="item.status == 'p'">⏳</template>
<template v-else-if="item.status == 'a'">👍</template>
<template v-else-if="item.status == 's'">📆</template>
<template v-else-if="item.status == 'c'">✔</template>
<template v-else-if="item.status == 'x'">❌</template>
<template v-else-if="item.status == 'dc'">🗑</template>
<template v-else>({{item.status}})</template>
{{item.text}}
</div>
</div>
</li>
</router-link>
</ul>
</div>
</div>
<div v-if="selection_text" class="col-12 col-xl-8">
<div class="card mb-3 shadow">
<div class="card-header d-flex justify-content-between align-items-center">
<span>{{doctitle(selection_text) || 'Consult'}} #{{selection}}</span>
<router-link class="close" :to="'/patient/' + patient_dfn + '/consult'" replace>❌</router-link>
</div>
<div class="detail card-body" ref="detail">{{selection_text}}</div>
</div>
</div>
</div>
</template>
<style scoped>
div.selector {
position: sticky;
top: 1.15rem;
z-index: 1;
}
ul.scroller.list-skinny {
max-height: 25vh;
overflow-y: auto;
}
li.record {
cursor: default;
border-top: 1px solid #dee2e6;
padding: 0.25rem 0.75rem;
scroll-margin-top: 3.6875rem;
}
li.record:nth-child(even) {
background-color: rgba(0, 0, 0, 0.05);
}
ul.scroller.list-skinny li.record {
scroll-margin-top: 0;
}
li.record a {
color: inherit;
}
li.record.active {
color: #fff;
background-color: #0d6efd;
}
li.bottom {
overflow-anchor: none;
}
div.cell {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
a.close {
cursor: default;
text-decoration: none;
}
div.detail {
scroll-margin-top: calc(3.6875rem + 2.5625rem + 25vh);
font-family: monospace;
white-space: pre-wrap;
}
@media (min-width: 1200px) {
div.selector {
position: static;
}
ul.scroller.list-skinny {
max-height: 75vh;
}
div.detail {
max-height: 75vh;
scroll-margin-top: 0;
overflow-y: auto;
}
}
</style>
<script>
import { debounce, strptime_vista } from './util.mjs';
import Subtitle from './Subtitle.vue';
const SZ_WINDOW = 100;
export default {
components: {
Subtitle
},
props: {
client: Object,
sensitive: Boolean,
patient_dfn: String,
patient_info: Object
},
data() {
return {
dfn: null,
has_more: '',
is_loading: false,
resultset: [],
selection: null,
selection_text: null
};
},
watch: {
'$route.params.ien': {
async handler(value) {
this.selection = value;
}, immediate: true
}
},
methods: {
strptime_vista,
datestring(date) {
return date.toLocaleDateString('sv-SE');
},
datetimestring(date) {
return date.toLocaleDateString('sv-SE') + ' ' + date.toLocaleTimeString('en-GB');
},
doctitle(doc) {
if(doc) {
var m = doc.match(/^Orderable Item:\s*(.*)$/m);
if(m) return m[1];
}
}
},
created() {
this.$watch(
() => (this.client, this.patient_dfn, {}),
debounce(async () => {
if((this.client) && (this.patient_dfn)) this.resultset = await this.client.ORQQCN_LIST(this.patient_dfn);
else this.resultset = [];
}, 500),
{ immediate: true }
);
this.$watch(
() => (this.client, this.selection, {}),
async function() {
try {
this.selection_text = (this.client) && (this.selection) ? await this.client.ORQQCN_DETAIL(this.selection) : null;
} catch(ex) {
this.selection_text = null;
console.warn(ex);
}
if(this.$refs.scroller) {
if(this.selection_text) { // scroll to selected item
await this.$nextTick();
var active = this.$refs.scroller.querySelectorAll(':scope > .active');
if(active.length > 0) (Element.prototype.scrollIntoViewIfNeeded || Element.prototype.scrollIntoView).call(active[0]);
if(this.$refs.detail) { // scroll to top of detail panel
this.$refs.detail.scrollIntoView();
this.$refs.detail.scrollTop = 0;
}
} else { // scroll to topmost item
var offset = this.$refs.scroller.getBoundingClientRect().top;
for(var children = this.$refs.scroller.children, count = children.length, i = 0; i < count; ++i) if(children[i].getBoundingClientRect().top >= offset) {
await this.$nextTick();
var behavior = document.documentElement.style.scrollBehavior;
document.documentElement.style.scrollBehavior = 'auto'; // inhibit Bootstrap smooth scrolling
children[i].scrollIntoView();
document.documentElement.style.scrollBehavior = behavior;
break;
}
}
}
},
{ immediate: true }
);
}
};
</script>