Recall list

This commit is contained in:
Jiang Yio 2022-09-23 23:59:55 -04:00
parent 25772419a0
commit 91f2c45e4f
3 changed files with 132 additions and 0 deletions

View File

@ -14,6 +14,7 @@
import RouteSchedule from './RouteSchedule.vue'; import RouteSchedule from './RouteSchedule.vue';
import RoutePatientLookup from './RoutePatientLookup.vue'; import RoutePatientLookup from './RoutePatientLookup.vue';
import RoutePatientDetail from './RoutePatientDetail.vue'; import RoutePatientDetail from './RoutePatientDetail.vue';
import RouteRecall from './RouteRecall.vue';
export default { export default {
components: { components: {
@ -42,6 +43,7 @@
{ path: '/', component: RouteSchedule, props: { client: this.client } }, { path: '/', component: RouteSchedule, props: { client: this.client } },
{ path: '/patient', component: RoutePatientLookup, props: { client: this.client } }, { path: '/patient', component: RoutePatientLookup, props: { client: this.client } },
{ path: '/patient/:id', component: RoutePatientDetail, props: { client: this.client } }, { path: '/patient/:id', component: RoutePatientDetail, props: { client: this.client } },
{ path: '/recall', component: RouteRecall, props: { client: this.client } },
].forEach(route => this.$root.$router.addRoute(route)); ].forEach(route => this.$root.$router.addRoute(route));
await this.$root.$router.replace(this.$route); await this.$root.$router.replace(this.$route);
} }

View File

@ -13,6 +13,9 @@
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/patient">Patient</a> <a class="nav-link" href="/patient">Patient</a>
</li> </li>
<li class="nav-item">
<a class="nav-link" href="/recall">Recall</a>
</li>
<li class="nav-item" v-if="user"> <li class="nav-item" v-if="user">
<a class="nav-link disabled">{{user[3]}}</a> <a class="nav-link disabled">{{user[3]}}</a>
</li> </li>

127
htdocs/RouteRecall.vue Normal file
View File

@ -0,0 +1,127 @@
<template>
<div>
<div class="card mb-3 shadow">
<div class="card-header">Clinics</div>
<div class="card-body">
<ViewResourceLookup :client="client" v-model:selection="selection" />
</div>
</div>
<div class="card mb-3 shadow">
<div class="card-header d-flex justify-content-between align-items-center">
<span>Recall list ({{patients_lost.length + patients_outstanding.length}})</span>
</div>
<div class="card-body">
<table class="table" style="font-family: monospace;" v-if="patients_lost && patients_lost.length > 0">
<thead>
<tr><th>Lost ({{patients_lost.length}})</th><th>Last appt</th><th>Clinic</th><th>Days</th></tr>
</thead>
<tbody>
<tr v-for="row in patients_lost" :style="{ backgroundColor: strHashHSL(row.Clinic, '90%') }">
<td v-if="production"><router-link :to="'/patient/$' + row.key">{{row.Name}} ${{row.key}}</router-link></td>
<td v-else><router-link :title="unscramble(row.Name)" :to="'/patient/$' + row.Name.charAt(0) + row.key.slice(-4) + '?name=' + row.Name">{{row.Name}} ${{row.key}}</router-link></td>
<td>{{datefmt(row.TimeLast)}} {{dow[row.TimeLast.getDay()]}}</td>
<td>{{row.Clinic}}</td>
<td>{{Math.round(row.TimeLastDiff/86400000)}}</td>
</tr>
</tbody>
</table>
<table class="table" style="font-family: monospace;" v-if="patients_outstanding && patients_outstanding.length > 0">
<thead>
<tr><th>Outstanding ({{patients_outstanding.length}})</th><th>Next appt</th><th>Clinic</th><th>Days</th></tr>
</thead>
<tbody>
<tr v-for="row in patients_outstanding" :style="{ backgroundColor: strHashHSL(row.Clinic, '90%') }">
<td v-if="production"><router-link :to="'/patient/$' + row.key">{{row.Name}} ${{row.key}}</router-link></td>
<td v-else><router-link :title="unscramble(row.Name)" :to="'/patient/$' + row.Name.charAt(0) + row.key.slice(-4) + '?name=' + row.Name">{{row.Name}} ${{row.key}}</router-link></td>
<td>{{datefmt(row.TimeNext)}} {{dow[row.TimeNext.getDay()]}}</td>
<td>{{row.Clinic}}</td>
<td>{{Math.round(row.TimeNextDiff/86400000)}}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</template>
<script>
import cookie from './cookie.mjs';
import { groupByArray, strHashHSL, strftime_vista } from './util.mjs';
import ViewResourceLookup from './ViewResourceLookup.vue';
function dateonly(date) {
return new Date(date.getFullYear(), date.getMonth(), date.getDate());
}
function strtr(s, a, b) {
var res = '';
for(var i = 0; i < s.length; ++i) {
var j = a.indexOf(s.charAt(i));
res += j >= 0 ? b.charAt(j) : s.charAt(i);
}
return res;
}
export default {
components: {
ViewResourceLookup
},
props: {
client: Object
},
data() {
var resources = cookie.get('vista.resources');
var today = dateonly(new Date());
return {
selection: resources ? (resources.split(',').filter(x => x) || []) : [],
patients: [],
production: true,
date_begin: new Date(today.getFullYear() - 1, today.getMonth(), today.getDate()),
date_end: new Date(today.getFullYear() + 1, today.getMonth(), today.getDate()),
dow: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
};
},
computed: {
patients_lost() {
return this.patients.filter(x => x.TimeLastDiff >= 0).sort((a, b) => b.TimeLastDiff - a.TimeLastDiff);
},
patients_outstanding() {
return this.patients.filter(x => x.TimeNextDiff >= 0).sort((a, b) => b.TimeNextDiff - a.TimeNextDiff);
}
},
watch: {
async selection(value) {
cookie.set('vista.resources', value.join(','), 7);
var patients = this.selection.length > 0 ? groupByArray(await this.client.SDEC_CLINLET(this.selection.join('|') + '|', strftime_vista(this.date_begin), strftime_vista(this.date_end)), x => x.HRN) : [], now = new Date(), group, values, appt;
for(var i = patients.length - 1; i >= 0; --i) {
group = patients[i];
group.Name = group.values[0].Name;
group.DOB = group.values[0].DOB;
group.values = values = group.values.map(function(x) { return { Time: new Date(x.ApptDate), Clinic: x.Clinic }; }).sort((a, b) => a.Time - b.Time);
group.TimeLast = (appt = group.values[group.values.length - 1]).Time;
group.TimeLastDiff = now - group.TimeLast;
group.Clinic = appt.Clinic;
if(group.TimeLastDiff < 0) for(var j = 0; j < values.length; ++j) if(values[j].Time - now > 0) {
group.TimeNext = values[j].Time;
group.TimeNextDiff = group.TimeNext - now;
break;
}
}
this.patients = patients.sort((a, b) => a.Time - b.Time);
}
},
methods: {
strHashHSL,
unscramble(name) {
return name.length > 0 ? (name.charAt(0) + strtr(name.substring(1), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'LKJIHGFEDCBAZYXWVUTSRQPONM')) : name;
},
datefmt(date) {
return date ? date.toLocaleDateString('en-CA') : '';
}
},
async mounted() {
this.production = (await this.client.serverinfo()).result.production == '1';
}
};
</script>