nuVistA/htdocs/RouteScheduleOverview.vue
2023-05-03 00:56:05 -04:00

147 lines
6.0 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div>
<div class="card mb-3 shadow">
<div class="card-header">Overview</div>
<div class="card-body">
<ViewResourceLookup :client="client" v-model:selection="selection" />
</div>
</div>
<div class="card mb-3 shadow" v-for="clinic in resultset">
<div class="card-header d-flex justify-content-between align-items-center">
<span>{{clinic.key}}</span>
</div>
<div class="card-body">
<table class="table">
<thead>
<tr><th v-for="x in dow">{{x}}</th></tr>
</thead>
<tbody>
<tr v-for="week in clinic.values">
<td v-for="day in [0, 1, 2, 3, 4, 5, 6]" class="datebox">
<template v-if="week.values[day]">
<div class="datebox" :style="{ backgroundColor: clinic.max > 0 ? 'rgba(220, 53, 69, ' + week.values[day].length/clinic.max + ')' : null }"><span class="occupancy hidden">#{{week.values[day].length}}</span> {{day > 0 ? week.values[day][0]._START_OBJ.getDate() : week.key.toLocaleDateString('sv-SE')}} <span class="occupancy">#{{week.values[day].length}}</span></div>
<template v-for="appointment in week.values[day]">
<div v-if="appointment._BREAK" class="vacancy" :title="appointment._START_OBJ.toLocaleTimeString('en-GB').substring(0, 5) + '' + appointment._END_OBJ.toLocaleTimeString('en-GB').substring(0, 5)" />
<div v-else :title="appointment._START_OBJ.toLocaleTimeString('en-GB').substring(0, 5) + '' + appointment._END_OBJ.toLocaleTimeString('en-GB').substring(0, 5) + '\n' + appointment.PATIENTNAME + ' ' + appointment.HRN.slice(-4) + '\n' + appointment.NOTE"><span v-if="appointment._CONCURRENCY > 0" class="concurrency hidden">*<template v-if="appointment._CONCURRENCY > 1">{{appointment._CONCURRENCY}}</template></span>{{appointment._START_OBJ.toLocaleTimeString('en-GB').substring(0, 5)}} {{appointment.PATIENTNAME.substring(0, 1)}}{{appointment.HRN.slice(-4)}}<span v-if="appointment._CONCURRENCY > 0" class="concurrency">*<template v-if="appointment._CONCURRENCY > 1">{{appointment._CONCURRENCY}}</template></span></div>
</template>
</template>
<div v-else class="datebox">{{day > 0 ? (new Date(week.key.getTime() + 1000*60*60*24*day)).getDate() : week.key.toLocaleDateString('sv-SE')}}</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</template>
<style scoped>
table {
font-family: monospace;
text-align: center;
}
tbody, td {
border: 1px solid #dee2e6;
}
div.datebox {
font-weight: bold;
}
div.vacancy {
width: 2em;
margin: auto;
border-bottom: 0.25em dotted #bbb;
}
span.occupancy {
font-weight: normal;
}
span.concurrency {
font-weight: bold;
}
span.hidden {
visibility: hidden;
}
</style>
<script>
import { groupBy, groupByArray, strfdate_vista, debounce } from './util.mjs';
import ViewResourceLookup from './ViewResourceLookup.vue';
const C_DAY = 1000*60*60*24;
const C_WEEK = C_DAY*7;
function infill_weeks(res, date_begin, date_end) {
for(var i = res.length - 1; i > 0; --i) {
var cur = res[i], prev = res[i - 1];
while(cur.key - prev.key > C_WEEK) res.splice(i, 0, cur = { key: new Date(cur.key.getTime() - C_WEEK), values: [] });
}
var item;
if(res.length < 1) res.push({ key: date_begin, values: [] });
else {
item = res[0];
while(item.key - date_begin >= C_WEEK) res.splice(0, 0, item = { key: new Date(item.key.getTime() - C_WEEK), values: [] });
}
item = res[res.length - 1];
while(date_end - item.key >= C_WEEK) res.push(item = { key: new Date(item.key.getTime() + C_WEEK), values: [] });
return res;
}
function analyze_week(res) {
for(var k in res) if(res.hasOwnProperty(k)) analyze_day(res[k]);
return res;
}
function analyze_day(res) {
for(var i = res.length - 1; i > 0; --i) {
var item = res[i];
item._CONCURRENCY = 0;
for(var j = i - 1; j >= 0; --j) if(item._START_OBJ < res[j]._END_OBJ) item._CONCURRENCY++;
if((item._CONCURRENCY < 1) && (item._START_OBJ > res[i - 1]._END_OBJ)) res.splice(i, 0, { _BREAK: true, _START_OBJ: res[i - 1]._END_OBJ, _END_OBJ: item._START_OBJ });
}
return res;
}
export default {
components: {
ViewResourceLookup
},
props: {
client: Object
},
data() {
var now = new Date();
return {
resultset: [],
date_begin: new Date(now.getFullYear(), now.getMonth(), now.getDate()),
date_end: new Date(now.getFullYear(), now.getMonth() + 1, now.getDate()),
dow: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
};
},
computed: {
selection: {
get() { return this.client.remotestate.resources ? (this.client.remotestate.resources.split(',').filter(x => x) || []) : [] },
set(value) { this.client.remotestate.resources = value.join(','); }
}
},
created() {
this.$watch(
() => (this.client, this.selection, {}),
debounce(async () => {
var date_begin = new Date(this.date_begin.getTime() - C_DAY*this.date_begin.getDay()), weekref = date_begin;
var date_end = new Date(this.date_end.getTime() + C_DAY*(6 - this.date_end.getDay()));
var resultset = this.selection.length < 1 ? [] : (await this.client.SDEC_CRSCHED(this.selection.join('|') + '|', strfdate_vista(date_begin), strfdate_vista(date_end) + '@2359')).filter(x => (x.CANCELLED == '0') && (x.NOSHOW == '0')).map(x => {
var _START_OBJ = x._START_OBJ = new Date(x.START_TIME);
x._START_DATE = _START_OBJ.toLocaleDateString('sv-SE');
x._END_OBJ = new Date(x.END_TIME);
x._WEEK_DAY = _START_OBJ.getDay();
x._WEEK_NUM = Math.floor((x._START_OBJ - weekref)/(C_WEEK));
return x;
}).sort((a, b) => a._START_OBJ - b._START_OBJ);
this.resultset = groupByArray(resultset, 'RESOURCENAME').sort((a, b) => a.key < b.key ? -1 : a.key > b.key ? 1 : 0).map(clinic => ({ key: clinic.key, max: Math.max.apply(null, groupByArray(clinic.values, '_START_DATE').map(x => x.values.length)), values: infill_weeks(groupByArray(clinic.values, '_WEEK_NUM').map(week => ({ key: new Date(weekref.getTime() + C_WEEK*week.key), values: analyze_week(groupBy(week.values, '_WEEK_DAY')) })), date_begin, date_end) }));
}, 500),
{ immediate: true }
);
}
};
</script>