Compare commits
	
		
			4 Commits
		
	
	
		
			770a9cfb2e
			...
			eebda06c86
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| eebda06c86 | |||
| 48a092432c | |||
| 4a3136a766 | |||
| 0f019ebc34 | 
@@ -7,7 +7,7 @@
 | 
				
			|||||||
				<div class="accordion-body" v-else>
 | 
									<div class="accordion-body" v-else>
 | 
				
			||||||
					<div class="card">
 | 
										<div class="card">
 | 
				
			||||||
						<div class="card-body">
 | 
											<div class="card-body">
 | 
				
			||||||
							<p class="card-text row"><code class="col" v-if="banner"><pre>{{banner.join('\n')}}</pre></code><code class="col" v-if="user"><pre>{{user.join('\n')}}</pre></code></p>
 | 
												<p class="card-text row"><code class="col" v-if="banner"><pre>{{banner.join('\n')}}</pre></code><code class="col" v-if="authinfo"><pre v-if="(authinfo.greeting) && (authinfo.greeting.length > 0)">{{authinfo.greeting.join('\n').replace(/^\n+|\s+$/gm, '')}}</pre><pre v-if="user">{{user.join('\n')}}</pre></code></p>
 | 
				
			||||||
						</div>
 | 
											</div>
 | 
				
			||||||
						<button class="btn btn-danger" style="width: 100%;" type="button" v-if="user" v-on:click="logout">Logout</button>
 | 
											<button class="btn btn-danger" style="width: 100%;" type="button" v-if="user" v-on:click="logout">Logout</button>
 | 
				
			||||||
						<div class="input-group flex-nowrap" v-if="!user">
 | 
											<div class="input-group flex-nowrap" v-if="!user">
 | 
				
			||||||
@@ -81,6 +81,7 @@
 | 
				
			|||||||
				x_server: this.server,
 | 
									x_server: this.server,
 | 
				
			||||||
				x_user: this.user,
 | 
									x_user: this.user,
 | 
				
			||||||
				banner: null,
 | 
									banner: null,
 | 
				
			||||||
 | 
									authinfo: null,
 | 
				
			||||||
				accesscode: null,
 | 
									accesscode: null,
 | 
				
			||||||
				verifycode: null
 | 
									verifycode: null
 | 
				
			||||||
			};
 | 
								};
 | 
				
			||||||
@@ -121,7 +122,7 @@
 | 
				
			|||||||
				this.logout();
 | 
									this.logout();
 | 
				
			||||||
				if(this.x_client = await (this.host ? vistax.Client.fromCookie(this.secret, this.host) : vistax.Client.fromCookie(this.secret))) {
 | 
									if(this.x_client = await (this.host ? vistax.Client.fromCookie(this.secret, this.host) : vistax.Client.fromCookie(this.secret))) {
 | 
				
			||||||
					this.banner = await this.x_client.XUS_INTRO_MSG();
 | 
										this.banner = await this.x_client.XUS_INTRO_MSG();
 | 
				
			||||||
					if((await this.x_client.userinfo()).result) try {
 | 
										if((this.authinfo = await this.x_client.authinfo()).success) try {
 | 
				
			||||||
						var user = await this.x_client.XUS_GET_USER_INFO();
 | 
											var user = await this.x_client.XUS_GET_USER_INFO();
 | 
				
			||||||
						this.x_user = user[0] ? user : null
 | 
											this.x_user = user[0] ? user : null
 | 
				
			||||||
					} catch(ex) {
 | 
										} catch(ex) {
 | 
				
			||||||
@@ -136,20 +137,32 @@
 | 
				
			|||||||
				} else {
 | 
									} else {
 | 
				
			||||||
					this.fail = true;
 | 
										this.fail = true;
 | 
				
			||||||
					this.host = undefined;
 | 
										this.host = undefined;
 | 
				
			||||||
 | 
										this.authinfo = null;
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			async login(evt) {
 | 
								async login(evt) {
 | 
				
			||||||
				if(this.x_client) {
 | 
									if(this.x_client) {
 | 
				
			||||||
					var res = await ((this.accesscode && this.verifycode) ? this.x_client.authenticate(this.accesscode + ';' + this.verifycode) : this.x_client.authenticate());
 | 
										try {
 | 
				
			||||||
					if(!!res.result[0]) {
 | 
											this.authinfo = await ((this.accesscode && this.verifycode) ? this.x_client.authenticate(this.accesscode + ';' + this.verifycode) : this.x_client.authenticate());
 | 
				
			||||||
 | 
											if(this.authinfo.duz) {
 | 
				
			||||||
							var user = await this.x_client.XUS_GET_USER_INFO();
 | 
												var user = await this.x_client.XUS_GET_USER_INFO();
 | 
				
			||||||
							this.x_user = user[0] ? user : null
 | 
												this.x_user = user[0] ? user : null
 | 
				
			||||||
					} else this.x_user = null;
 | 
											} else {
 | 
				
			||||||
 | 
												this.x_user = null;
 | 
				
			||||||
 | 
												if(this.authinfo.message) window.alert(this.authinfo.message);
 | 
				
			||||||
 | 
												else window.alert('Authentication failed.');
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										} catch(ex) {
 | 
				
			||||||
 | 
											this.authinfo = this.x_user = null;
 | 
				
			||||||
 | 
											console.warn(ex);
 | 
				
			||||||
 | 
											if(ex.name) window.alert(ex.name);
 | 
				
			||||||
 | 
											else window.alert('Authentication failed.');
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
					this.$emit('update:user', this.x_user);
 | 
										this.$emit('update:user', this.x_user);
 | 
				
			||||||
					this.show = !this.x_user;
 | 
										this.show = !this.x_user;
 | 
				
			||||||
					this.$emit('update:server', this.x_server = (await this.x_client.serverinfo()).result);
 | 
										this.$emit('update:server', this.x_server = (await this.x_client.serverinfo()).result);
 | 
				
			||||||
					console.log('Authenticate', res);
 | 
										console.log('Authenticate', this.authinfo);
 | 
				
			||||||
				}
 | 
									} else this.authinfo = null;
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			async logout(evt) {
 | 
								async logout(evt) {
 | 
				
			||||||
				if(this.x_client) {
 | 
									if(this.x_client) {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										84
									
								
								htdocs/ModalPromptSignatureCode.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								htdocs/ModalPromptSignatureCode.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,84 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
						<TransitionGroup>
 | 
				
			||||||
 | 
							<div v-if="x_show" class="modal show" style="display: block;" tabindex="-1" @keydown.enter="submit" @keydown.esc="cancel">
 | 
				
			||||||
 | 
								<div class="modal-dialog">
 | 
				
			||||||
 | 
									<div class="modal-content">
 | 
				
			||||||
 | 
										<div class="modal-header">
 | 
				
			||||||
 | 
											<h5 class="modal-title">{{label || 'Sign'}}</h5>
 | 
				
			||||||
 | 
											<button type="button" class="btn-close" @click="cancel"></button>
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
										<div class="modal-body">
 | 
				
			||||||
 | 
											<div class="input-group">
 | 
				
			||||||
 | 
												<span class="input-group-text">Code</span>
 | 
				
			||||||
 | 
												<input ref="input" type="password" class="form-control" :class="{ 'is-invalid': valid === false }" v-model="x_modelValue" @input="() => valid = null" />
 | 
				
			||||||
 | 
												<div v-if="valid === false" class="invalid-feedback">Invalid code.</div>
 | 
				
			||||||
 | 
											</div>
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
										<div class="modal-footer">
 | 
				
			||||||
 | 
											<button type="button" class="btn btn-primary" :disabled="!x_modelValue" @click="submit">Submit</button>
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
							</div>
 | 
				
			||||||
 | 
							<div v-if="x_show" class="modal-backdrop show"></div>
 | 
				
			||||||
 | 
						</TransitionGroup>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style scoped>
 | 
				
			||||||
 | 
						.v-enter-active, .v-leave-active {
 | 
				
			||||||
 | 
							transition: opacity 0.25s ease;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						.v-enter-from, .v-leave-to {
 | 
				
			||||||
 | 
							opacity: 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script>
 | 
				
			||||||
 | 
						export default {
 | 
				
			||||||
 | 
							props: {
 | 
				
			||||||
 | 
								client: Object,
 | 
				
			||||||
 | 
								show: {
 | 
				
			||||||
 | 
									type: Boolean,
 | 
				
			||||||
 | 
									default: false
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								label: String,
 | 
				
			||||||
 | 
								modelValue: null
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							emits: {
 | 
				
			||||||
 | 
								'cancel': null,
 | 
				
			||||||
 | 
								'submit': String,
 | 
				
			||||||
 | 
								'update:show': String,
 | 
				
			||||||
 | 
								'update:modelValue': null
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							data() {
 | 
				
			||||||
 | 
								return {
 | 
				
			||||||
 | 
									valid: null,
 | 
				
			||||||
 | 
									x_show: this.show,
 | 
				
			||||||
 | 
									x_modelValue: this.modelValue
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							watch: {
 | 
				
			||||||
 | 
								show(value) { this.x_show = value; },
 | 
				
			||||||
 | 
								async x_show(value) {
 | 
				
			||||||
 | 
									this.x_modelValue = '';
 | 
				
			||||||
 | 
									this.$emit('update:show', value);
 | 
				
			||||||
 | 
									await this.$nextTick();
 | 
				
			||||||
 | 
									if((value) && (this.$refs.input)) this.$refs.input.focus();
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								modelValue(value) { this.x_modelValue = value; },
 | 
				
			||||||
 | 
								x_modelValue(value) { this.$emit('update:modelValue', value); }
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							methods: {
 | 
				
			||||||
 | 
								cancel() { this.x_show = false; },
 | 
				
			||||||
 | 
								async submit() {
 | 
				
			||||||
 | 
									var value = this.x_modelValue;
 | 
				
			||||||
 | 
									if((this.client) && (value)) {
 | 
				
			||||||
 | 
										if(this.valid = (await this.client.ORWU_VALIDSIG(' ' + value + ' ')) == '1') {
 | 
				
			||||||
 | 
											this.x_show = false;
 | 
				
			||||||
 | 
											this.$emit('submit', value);
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
@@ -2,15 +2,23 @@
 | 
				
			|||||||
	<Subtitle value="Documents" />
 | 
						<Subtitle value="Documents" />
 | 
				
			||||||
	<Subtitle :value="patient_info.name" />
 | 
						<Subtitle :value="patient_info.name" />
 | 
				
			||||||
	<div class="row">
 | 
						<div class="row">
 | 
				
			||||||
		<div class="selector col-12" :class="{ 'col-xl-4': selection_text }">
 | 
							<div class="selector col-12" :class="{ 'col-xl-4': selection }">
 | 
				
			||||||
			<div class="card mb-3 shadow">
 | 
								<div class="card mb-3 shadow">
 | 
				
			||||||
				<div class="card-header"><template v-if="resultset.length > 0">{{resultset.length}}<template v-if="has_more">+</template></template><template v-else-if="is_loading">Loading</template><template v-else>No</template> record{{resultset.length == 1 ? '' : 's'}}</div>
 | 
									<div class="card-header d-flex justify-content-between align-items-center">
 | 
				
			||||||
				<ul class="scroller list-group list-group-flush" :class="{ 'list-skinny': selection_text }" ref="scroller">
 | 
										<span><template v-if="resultset.length > 0">{{resultset.length}}<template v-if="has_more">+</template></template><template v-else-if="is_loading">Loading</template><template v-else>No</template> record{{resultset.length == 1 ? '' : 's'}}</span>
 | 
				
			||||||
 | 
										<router-link :to="'/patient/' + patient_dfn + '/document/new'">
 | 
				
			||||||
 | 
											<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 16 16" style="width: 1.25em; height: 1.25em; vertical-align: text-bottom;">
 | 
				
			||||||
 | 
												<path d="M14 1a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h12zM2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2z"/>
 | 
				
			||||||
 | 
												<path d="M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3A.5.5 0 0 1 8 4z"/>
 | 
				
			||||||
 | 
											</svg>
 | 
				
			||||||
 | 
										</router-link>
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
									<ul class="scroller list-group list-group-flush" :class="{ 'list-skinny': selection }" ref="scroller">
 | 
				
			||||||
					<router-link v-for="item in resultset" :to="'/patient/' + patient_dfn + '/document/' + item.IEN" replace custom v-slot="{ navigate, href }">
 | 
										<router-link v-for="item in resultset" :to="'/patient/' + patient_dfn + '/document/' + 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)) + '\n' + item.title + '\n' + item.location + '\n' + item.author.byline" @click="navigate">
 | 
											<li :key="item" class="record list-group-item" :class="{ 'active': selection == item.IEN }" :title="datetimestring(strptime_vista(item.time)) + '\n' + item.title + '\n' + item.location + '\n' + item.author.byline" @click="navigate">
 | 
				
			||||||
							<div class="row">
 | 
												<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-4"><router-link :to="href" replace>{{datestring(strptime_vista(item.time))}}</router-link></div>
 | 
				
			||||||
								<div class="cell col-8">{{item.title}}</div>
 | 
													<div class="cell col-8"><template v-if="item.status == 'unsigned'">✱</template>{{item.title}}</div>
 | 
				
			||||||
								<div class="cell secondary col-7 col-lg-4 col-xl-7">{{item.location}}</div>
 | 
													<div class="cell secondary col-7 col-lg-4 col-xl-7">{{item.location}}</div>
 | 
				
			||||||
								<div class="cell secondary col-5 col-lg-8 col-xl-5">{{item.author.byline}}</div>
 | 
													<div class="cell secondary col-5 col-lg-8 col-xl-5">{{item.author.byline}}</div>
 | 
				
			||||||
							</div>
 | 
												</div>
 | 
				
			||||||
@@ -20,16 +28,14 @@
 | 
				
			|||||||
				</ul>
 | 
									</ul>
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
		<div v-if="selection_text" class="col-12 col-xl-8">
 | 
							<div v-if="selection == 'new'" class="col-12 col-xl-8">
 | 
				
			||||||
			<div class="card mb-3 shadow">
 | 
								<ViewDocNew :client="client" :dfn="patient_dfn" :datetime="datetimestring(new Date())" @cancel="() => $router.replace({ path: '/patient/' + patient_dfn + '/document' })" @submit="doc_create" />
 | 
				
			||||||
				<div class="card-header d-flex justify-content-between align-items-center">
 | 
					 | 
				
			||||||
					<span>{{doctitle(selection_text) || 'Document'}}</span>
 | 
					 | 
				
			||||||
					<router-link class="close" :to="'/patient/' + patient_dfn + '/document'" replace>❌</router-link>
 | 
					 | 
				
			||||||
				</div>
 | 
					 | 
				
			||||||
				<div class="detail card-body" ref="detail">{{selection_text}}</div>
 | 
					 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
 | 
							<div v-else-if="selection" class="detail col-12 col-xl-8" ref="detail">
 | 
				
			||||||
 | 
								<ViewDocView :client="client" :dfn="patient_dfn" :ien="selection" @sign="doc_sign_prompt" @delete="doc_delete" @cancel="() => $router.replace({ path: '/patient/' + patient_dfn + '/document' })" />
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
	</div>
 | 
						</div>
 | 
				
			||||||
 | 
						<ModalPromptSignatureCode :client="client" v-model:show="show_signature" @submit="doc_sign" label="Sign Document" />
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style scoped>
 | 
					<style scoped>
 | 
				
			||||||
@@ -73,7 +79,7 @@
 | 
				
			|||||||
		cursor: default;
 | 
							cursor: default;
 | 
				
			||||||
		text-decoration: none;
 | 
							text-decoration: none;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	div.detail {
 | 
						div.detail /deep/ .card-body {
 | 
				
			||||||
		scroll-margin-top: calc(3.6875rem + 2.5625rem + 25vh);
 | 
							scroll-margin-top: calc(3.6875rem + 2.5625rem + 25vh);
 | 
				
			||||||
		font-family: monospace;
 | 
							font-family: monospace;
 | 
				
			||||||
		white-space: pre-wrap;
 | 
							white-space: pre-wrap;
 | 
				
			||||||
@@ -88,7 +94,7 @@
 | 
				
			|||||||
		div.cell.secondary {
 | 
							div.cell.secondary {
 | 
				
			||||||
			font-size: 0.8em;
 | 
								font-size: 0.8em;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		div.detail {
 | 
							div.detail /deep/ .card-body {
 | 
				
			||||||
			max-height: 75vh;
 | 
								max-height: 75vh;
 | 
				
			||||||
			scroll-margin-top: 0;
 | 
								scroll-margin-top: 0;
 | 
				
			||||||
			overflow-y: auto;
 | 
								overflow-y: auto;
 | 
				
			||||||
@@ -100,12 +106,15 @@
 | 
				
			|||||||
	import { debounce, strptime_vista } from './util.mjs';
 | 
						import { debounce, strptime_vista } from './util.mjs';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	import Subtitle from './Subtitle.vue';
 | 
						import Subtitle from './Subtitle.vue';
 | 
				
			||||||
 | 
						import ViewDocNew from './ViewDocNew.vue';
 | 
				
			||||||
 | 
						import ViewDocView from './ViewDocView.vue';
 | 
				
			||||||
 | 
						import ModalPromptSignatureCode from './ModalPromptSignatureCode.vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const SZ_WINDOW = 100;
 | 
						const SZ_WINDOW = 100;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	export default {
 | 
						export default {
 | 
				
			||||||
		components: {
 | 
							components: {
 | 
				
			||||||
			Subtitle
 | 
								Subtitle, ViewDocNew, ViewDocView, ModalPromptSignatureCode
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		props: {
 | 
							props: {
 | 
				
			||||||
			client: Object,
 | 
								client: Object,
 | 
				
			||||||
@@ -118,13 +127,19 @@
 | 
				
			|||||||
				dfn: null,
 | 
									dfn: null,
 | 
				
			||||||
				has_more: '',
 | 
									has_more: '',
 | 
				
			||||||
				is_loading: false,
 | 
									is_loading: false,
 | 
				
			||||||
				resultset: [],
 | 
									rs_unsigned: [],
 | 
				
			||||||
 | 
									rs_signed: [],
 | 
				
			||||||
				selection: null,
 | 
									selection: null,
 | 
				
			||||||
				selection_text: null,
 | 
									show_signature: false,
 | 
				
			||||||
				observer_scroller: null,
 | 
									observer_scroller: null,
 | 
				
			||||||
				observer_viewport: null
 | 
									observer_viewport: null
 | 
				
			||||||
			};
 | 
								};
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							computed: {
 | 
				
			||||||
 | 
								resultset() {
 | 
				
			||||||
 | 
									return this.rs_unsigned.concat(this.rs_signed);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
		watch: {
 | 
							watch: {
 | 
				
			||||||
			'$route.params.tiu_da': {
 | 
								'$route.params.tiu_da': {
 | 
				
			||||||
				async handler(value) {
 | 
									async handler(value) {
 | 
				
			||||||
@@ -140,17 +155,6 @@
 | 
				
			|||||||
			datetimestring(date) {
 | 
								datetimestring(date) {
 | 
				
			||||||
				return date.toLocaleDateString('sv-SE') + ' ' + date.toLocaleTimeString('en-GB');
 | 
									return date.toLocaleDateString('sv-SE') + ' ' + date.toLocaleTimeString('en-GB');
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			doctitle(doc) {
 | 
					 | 
				
			||||||
				if(doc) {
 | 
					 | 
				
			||||||
					var brk = doc.indexOf('\r\n');
 | 
					 | 
				
			||||||
					if(brk >= 0) {
 | 
					 | 
				
			||||||
						doc = doc.substring(0, brk);
 | 
					 | 
				
			||||||
						brk = doc.indexOf(': ');
 | 
					 | 
				
			||||||
						if(brk >= 0) return doc.substring(brk + 2).replace(/^\s+|\s+$/g, '');
 | 
					 | 
				
			||||||
						else return doc.replace(/^\s+|\s+$/g, '');
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			async load_more() {
 | 
								async load_more() {
 | 
				
			||||||
				try {
 | 
									try {
 | 
				
			||||||
					this.is_loading = true;
 | 
										this.is_loading = true;
 | 
				
			||||||
@@ -158,7 +162,7 @@
 | 
				
			|||||||
						if(this.dfn != this.patient_dfn) {
 | 
											if(this.dfn != this.patient_dfn) {
 | 
				
			||||||
							this.dfn = this.patient_dfn;
 | 
												this.dfn = this.patient_dfn;
 | 
				
			||||||
							this.has_more = '';
 | 
												this.has_more = '';
 | 
				
			||||||
							this.resultset = [];
 | 
												this.rs_signed = [];
 | 
				
			||||||
						}
 | 
											}
 | 
				
			||||||
						var res = await client.TIU_DOCUMENTS_BY_CONTEXT(3, 1, this.patient_dfn, -1, -1, 0, SZ_WINDOW, 'D', 1, 0, 1, this.has_more);
 | 
											var res = await client.TIU_DOCUMENTS_BY_CONTEXT(3, 1, this.patient_dfn, -1, -1, 0, SZ_WINDOW, 'D', 1, 0, 1, this.has_more);
 | 
				
			||||||
						if((res) && (res.length > 0)) {
 | 
											if((res) && (res.length > 0)) {
 | 
				
			||||||
@@ -168,38 +172,81 @@
 | 
				
			|||||||
								this.has_more = last.IEN;
 | 
													this.has_more = last.IEN;
 | 
				
			||||||
								res.splice(res.length - 1, 1);
 | 
													res.splice(res.length - 1, 1);
 | 
				
			||||||
							}
 | 
												}
 | 
				
			||||||
							if(this.resultset.length > 0) Array.prototype.push.apply(this.resultset, res);
 | 
												if(this.rs_signed.length > 0) Array.prototype.push.apply(this.rs_signed, res);
 | 
				
			||||||
							else this.resultset = res;
 | 
												else this.rs_signed = res;
 | 
				
			||||||
						}
 | 
											}
 | 
				
			||||||
					} else {
 | 
										} else {
 | 
				
			||||||
						this.dfn = null;
 | 
											this.dfn = null;
 | 
				
			||||||
						this.has_more = '';
 | 
											this.has_more = '';
 | 
				
			||||||
						this.resultset = [];
 | 
											this.rs_signed = [];
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
				} catch(ex) {
 | 
									} catch(ex) {
 | 
				
			||||||
					console.warn(ex);
 | 
										console.warn(ex);
 | 
				
			||||||
				} finally {
 | 
									} finally {
 | 
				
			||||||
					this.is_loading = false;
 | 
										this.is_loading = false;
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								async load_unsigned() {
 | 
				
			||||||
 | 
									this.rs_unsigned = [];
 | 
				
			||||||
 | 
									this.rs_unsigned = await client.TIU_DOCUMENTS_BY_CONTEXT(3, 2, this.patient_dfn, 0, 0, 0, 0, 'D', 1, 0, 1, '');
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								async reload() {
 | 
				
			||||||
 | 
									this.dfn = null;
 | 
				
			||||||
 | 
									await client.TIU_DOCUMENTS_BY_CONTEXT_FLUSH(3, 2, this.patient_dfn, 0, 0, 0, 0, 'D', 1, 0, 1, '');
 | 
				
			||||||
 | 
									await this.load_unsigned();
 | 
				
			||||||
 | 
									await client.TIU_DOCUMENTS_BY_CONTEXT_FLUSH(3, 1, this.patient_dfn, -1, -1, 0, SZ_WINDOW, 'D', 1, 0, 1, '');
 | 
				
			||||||
 | 
									await this.load_more();
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								async doc_create(params) {
 | 
				
			||||||
 | 
									var vstr = params.location.datetime ? ('' + params.location.IEN + ';' + params.location.datetime + ';A') : ('' + params.location.IEN + ';' + params.datetime + ';E');
 | 
				
			||||||
 | 
									var res = await this.client.TIU_CREATE_RECORD(this.patient_dfn, params.title, '', '', '', { '".01"': params.title, '"1202"': params.author, '"1301"': params.datetime, '"1205"': params.location.IEN }, vstr, '1');
 | 
				
			||||||
 | 
									if(res) {
 | 
				
			||||||
 | 
										this.reload();
 | 
				
			||||||
 | 
										this.$router.replace({ path: '/patient/' + this.patient_dfn + '/document/' + res, query: { edit: '' } });
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										console.error('Unable to create document', params, res);
 | 
				
			||||||
 | 
										window.alert('Unable to create document.');
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								doc_sign_prompt(ien) {
 | 
				
			||||||
 | 
									this.show_signature = true;
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								async doc_sign(code) {
 | 
				
			||||||
 | 
									var selection = this.selection;
 | 
				
			||||||
 | 
									if((selection) & (code)) {
 | 
				
			||||||
 | 
										this.show_signature = false;
 | 
				
			||||||
 | 
										await this.client.TIU_SIGN_RECORD(selection, ' ' + code + ' ');
 | 
				
			||||||
 | 
										this.reload();
 | 
				
			||||||
 | 
										this.selection = null;
 | 
				
			||||||
 | 
										await this.$nextTick();
 | 
				
			||||||
 | 
										this.selection = selection;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								async doc_delete(ien) {
 | 
				
			||||||
 | 
									if(window.confirm('Delete this document?')) {
 | 
				
			||||||
 | 
										var vstr = await this.client.ORWPCE_NOTEVSTR(ien);
 | 
				
			||||||
 | 
										if(vstr) await this.client.ORWPCE_DELETE(vstr, this.patient_dfn);
 | 
				
			||||||
 | 
										await this.client.TIU_DELETE_RECORD(ien);
 | 
				
			||||||
 | 
										this.reload();
 | 
				
			||||||
 | 
										if(this.selection == ien) {
 | 
				
			||||||
 | 
											this.selection = null;
 | 
				
			||||||
 | 
											this.$router.replace({ path: '/patient/' + this.patient_dfn + '/document' });
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		created() {
 | 
							created() {
 | 
				
			||||||
			this.$watch(
 | 
								this.$watch(
 | 
				
			||||||
				() => (this.client, this.patient_dfn, {}),
 | 
									() => (this.client, this.patient_dfn, {}),
 | 
				
			||||||
				debounce(this.load_more, 500),
 | 
									debounce(() => { this.load_more(); this.load_unsigned(); }, 500),
 | 
				
			||||||
				{ immediate: true }
 | 
									{ immediate: true }
 | 
				
			||||||
			);
 | 
								);
 | 
				
			||||||
			this.$watch(
 | 
								this.$watch(
 | 
				
			||||||
				() => (this.client, this.selection, {}),
 | 
									() => (this.client, this.selection, {}),
 | 
				
			||||||
				async function() {
 | 
									async function() {
 | 
				
			||||||
					try {
 | 
					 | 
				
			||||||
						this.selection_text = (this.client) && (this.selection) ? await this.client.TIU_GET_RECORD_TEXT(this.selection) : null;
 | 
					 | 
				
			||||||
					} catch(ex) {
 | 
					 | 
				
			||||||
						this.selection_text = null;
 | 
					 | 
				
			||||||
						console.warn(ex);
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
					if(this.$refs.scroller) {
 | 
										if(this.$refs.scroller) {
 | 
				
			||||||
						if(this.selection_text) {	// scroll to selected item
 | 
											if(this.selection) {	// scroll to selected item
 | 
				
			||||||
 | 
												if(this.selection != 'new') {
 | 
				
			||||||
								await this.$nextTick();
 | 
													await this.$nextTick();
 | 
				
			||||||
								var active = this.$refs.scroller.querySelectorAll(':scope > .active');
 | 
													var active = this.$refs.scroller.querySelectorAll(':scope > .active');
 | 
				
			||||||
								if(active.length > 0) (Element.prototype.scrollIntoViewIfNeeded || Element.prototype.scrollIntoView).call(active[0]);
 | 
													if(active.length > 0) (Element.prototype.scrollIntoViewIfNeeded || Element.prototype.scrollIntoView).call(active[0]);
 | 
				
			||||||
@@ -207,6 +254,7 @@
 | 
				
			|||||||
									this.$refs.detail.scrollIntoView();
 | 
														this.$refs.detail.scrollIntoView();
 | 
				
			||||||
									this.$refs.detail.scrollTop = 0;
 | 
														this.$refs.detail.scrollTop = 0;
 | 
				
			||||||
								}
 | 
													}
 | 
				
			||||||
 | 
												}
 | 
				
			||||||
						} else { // scroll to topmost item
 | 
											} else { // scroll to topmost item
 | 
				
			||||||
							var offset = this.$refs.scroller.getBoundingClientRect().top;
 | 
												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) {
 | 
												for(var children = this.$refs.scroller.children, count = children.length, i = 0; i < count; ++i) if(children[i].getBoundingClientRect().top >= offset) {
 | 
				
			||||||
@@ -224,9 +272,9 @@
 | 
				
			|||||||
			);
 | 
								);
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		mounted() {
 | 
							mounted() {
 | 
				
			||||||
			this.observer_scroller = new IntersectionObserver(([entry]) => { if((entry.isIntersecting) && (this.selection_text) && (this.has_more) && (!this.is_loading)) this.load_more(); }, { root: this.$refs.scroller, rootMargin: '25%' });
 | 
								this.observer_scroller = new IntersectionObserver(([entry]) => { if((entry.isIntersecting) && (this.selection) && (this.has_more) && (!this.is_loading)) this.load_more(); }, { root: this.$refs.scroller, rootMargin: '25%' });
 | 
				
			||||||
			this.observer_scroller.observe(this.$refs.bottom);
 | 
								this.observer_scroller.observe(this.$refs.bottom);
 | 
				
			||||||
			this.observer_viewport = new IntersectionObserver(([entry]) => { if((entry.isIntersecting) && (!this.selection_text) && (this.has_more) && (!this.is_loading)) this.load_more(); }, { rootMargin: '25%' });
 | 
								this.observer_viewport = new IntersectionObserver(([entry]) => { if((entry.isIntersecting) && (!this.selection) && (this.has_more) && (!this.is_loading)) this.load_more(); }, { rootMargin: '25%' });
 | 
				
			||||||
			this.observer_viewport.observe(this.$refs.bottom);
 | 
								this.observer_viewport.observe(this.$refs.bottom);
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		destroyed() {
 | 
							destroyed() {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										177
									
								
								htdocs/ViewDocEdit.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										177
									
								
								htdocs/ViewDocEdit.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,177 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
						<div v-if="record !== null" class="card mb-3 shadow">
 | 
				
			||||||
 | 
							<div class="card-header d-flex justify-content-between align-items-center">
 | 
				
			||||||
 | 
								<span>{{record['~.01'] && record['~.01'].description || 'Document'}}</span>
 | 
				
			||||||
 | 
								<a class="widget" @click="() => update(true)">✔</a>
 | 
				
			||||||
 | 
							</div>
 | 
				
			||||||
 | 
							<ul class="list-group list-group-flush">
 | 
				
			||||||
 | 
								<li class="list-group-item"><DateTimePicker v-model="datetime" /></li>
 | 
				
			||||||
 | 
								<li class="list-group-item">
 | 
				
			||||||
 | 
									<div class="input-group">
 | 
				
			||||||
 | 
										<span class="input-group-text">Subject</span>
 | 
				
			||||||
 | 
										<input type="text" class="form-control" v-model="subject" />
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
								</li>
 | 
				
			||||||
 | 
								<li class="list-group-item"><textarea ref="textarea" class="form-control" v-model="text" @keydown.tab.exact.prevent="tab" @keydown.shift.tab.exact.prevent="untab" /></li>
 | 
				
			||||||
 | 
							</ul>
 | 
				
			||||||
 | 
							<div v-if="saved" class="card-footer" style="text-align: right;">Saved at {{saved.toLocaleString()}}</div>
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style scoped>
 | 
				
			||||||
 | 
						a.widget {
 | 
				
			||||||
 | 
							cursor: default;
 | 
				
			||||||
 | 
							text-decoration: none;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						textarea {
 | 
				
			||||||
 | 
							font-family: monospace;
 | 
				
			||||||
 | 
							tab-size: 8;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script>
 | 
				
			||||||
 | 
						import { debounce } from './util.mjs';
 | 
				
			||||||
 | 
						import { strptime, strftime } from './fmdatetime.mjs';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						import DateTimePicker from './DateTimePicker.vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						function untab({ input, size=8, tab='\t', space=' ', join=true}={}) {
 | 
				
			||||||
 | 
							input = input.split('\n');
 | 
				
			||||||
 | 
							for(var i = input.length - 1; i >= 0; --i) input[i] = untab_line(input[i], size, tab, space);
 | 
				
			||||||
 | 
							return join ? input.join('\n') : input;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						function untab_line(line, size=8, tab='\t', space=' ') {
 | 
				
			||||||
 | 
							var res = '', index = 0, offset = 0, next, count;
 | 
				
			||||||
 | 
							while((next = line.indexOf(tab, index)) >= 0) {
 | 
				
			||||||
 | 
								count = size - (next + offset)%size;
 | 
				
			||||||
 | 
								res += line.substring(index, next) + Array(count + 1).join(space);
 | 
				
			||||||
 | 
								offset += count - 1;
 | 
				
			||||||
 | 
								index = next + 1;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return res + line.substring(index);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						function retab({ input, size=8, tab='\t', space=' ' }={}) {
 | 
				
			||||||
 | 
							var re_space = new RegExp(space.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + '+', 'g');
 | 
				
			||||||
 | 
							return input.replace(re_space, function(m) { return Array(Math.ceil(m.length/size) + 1).join(tab); });
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						function wrap({ input, width=80, cut=false, tabsize=8, tab='\t', space=' ', untab=false, join=true }={}) {
 | 
				
			||||||
 | 
							var input = input.split('\n'), lines, res = [];
 | 
				
			||||||
 | 
							if(untab) {
 | 
				
			||||||
 | 
								for(var i = 0; i < input.length; ++i) {
 | 
				
			||||||
 | 
									lines = wrap_line_split(untab_line(input[i], tabsize, tab, space), width, cut);
 | 
				
			||||||
 | 
									for(var j = 0; j < lines.length; ++j) res.push(lines[j].replace(/(\s)\s+$/, '$1'));
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return join ? res.join('\n') : res;
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								for(var i = 0; i < input.length; ++i) {
 | 
				
			||||||
 | 
									lines = wrap_line_split(untab_line(input[i], tabsize, tab, tab), width, cut);	// replace tabs with placeholder tabs
 | 
				
			||||||
 | 
									for(var j = 0; j < lines.length; ++j) res.push(lines[j].replace(/(\s)\s+$/, '$1'));
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								res = retab({ input: res.join('\n'), size: tabsize, tab, space: tab });	// collapse placeholder tabs
 | 
				
			||||||
 | 
								return join ? res : res.split('\n');
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						function wrap_line(str, width=80, cut=false, brk='\n') {
 | 
				
			||||||
 | 
							if(!str) return str;
 | 
				
			||||||
 | 
							return str.match(new RegExp('.{1,' + width + '}(\\s+|$)' + (cut ? '|.{' + width + '}|.+$' : '|\\S+?(\\s+|$)'), 'g')).join(brk);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						function wrap_line_split(str, width=80, cut=false) {
 | 
				
			||||||
 | 
							if(!str) return [str];
 | 
				
			||||||
 | 
							return str.match(new RegExp('.{1,' + width + '}(\\s+|$)' + (cut ? '|.{' + width + '}|.+$' : '|\\S+?(\\s+|$)'), 'g'));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						export default {
 | 
				
			||||||
 | 
							components: {
 | 
				
			||||||
 | 
								DateTimePicker
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							props: {
 | 
				
			||||||
 | 
								client: Object,
 | 
				
			||||||
 | 
								dfn: String,
 | 
				
			||||||
 | 
								ien: String
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							emits: {
 | 
				
			||||||
 | 
								'accept': null,
 | 
				
			||||||
 | 
								'update': Object
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							data() {
 | 
				
			||||||
 | 
								return {
 | 
				
			||||||
 | 
									record: null,
 | 
				
			||||||
 | 
									saved: null
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							computed: {
 | 
				
			||||||
 | 
								datetime: {
 | 
				
			||||||
 | 
									get() { return this.record && this.record['~1301'] && this.record['~1301'].value; },
 | 
				
			||||||
 | 
									set(value) { this.record['~1301'].value = strftime(strptime(value)); this.autosave(); }
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								subject: {
 | 
				
			||||||
 | 
									get() { return this.record && this.record['~1701'] && this.record['~1701'].value; },
 | 
				
			||||||
 | 
									set(value) { this.record['~1701'].value = this.record['~.07'].value = value; this.autosave(); }
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								text: {
 | 
				
			||||||
 | 
									get() { return this.record && this.record.text; },
 | 
				
			||||||
 | 
									set(value) { this.record.text = value; this.autosave(); }
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							watch: {
 | 
				
			||||||
 | 
								text() {
 | 
				
			||||||
 | 
									this.resize();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							methods: {
 | 
				
			||||||
 | 
								async tab(evt) {
 | 
				
			||||||
 | 
									var target = event.target, value = target.value, start = target.selectionStart, end = target.selectionEnd;
 | 
				
			||||||
 | 
									if(start == end) document.execCommand('insertText', false, '\t');
 | 
				
			||||||
 | 
									else {
 | 
				
			||||||
 | 
										start = target.selectionStart = value.lastIndexOf('\n', start - 1) + 1;
 | 
				
			||||||
 | 
										end = target.selectionEnd = value.indexOf('\n', end); if(end < 0) end = value.length;
 | 
				
			||||||
 | 
										var selection = value.substring(start, end);
 | 
				
			||||||
 | 
										document.execCommand('insertText', false, selection.replace(/^/gm, '\t'));
 | 
				
			||||||
 | 
										await this.$nextTick();
 | 
				
			||||||
 | 
										target.selectionStart = start;
 | 
				
			||||||
 | 
										target.selectionEnd = end + selection.split('\n').length;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								async untab(evt) {
 | 
				
			||||||
 | 
									var target = event.target, value = target.value;
 | 
				
			||||||
 | 
									var start = target.selectionStart = value.lastIndexOf('\n', target.selectionStart - 1) + 1;
 | 
				
			||||||
 | 
									var end = target.selectionEnd = value.indexOf('\n', target.selectionEnd); if(end < 0) end = value.length;
 | 
				
			||||||
 | 
									var selection = value.substring(start, end);
 | 
				
			||||||
 | 
									document.execCommand('insertText', false, selection.replace(/^\t/gm, ''));
 | 
				
			||||||
 | 
									await this.$nextTick();
 | 
				
			||||||
 | 
									target.selectionStart = start;
 | 
				
			||||||
 | 
									target.selectionEnd = end - (selection.match(/^\t/gm) || []).length;
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								async update(accept) {
 | 
				
			||||||
 | 
									var res = this.record.reduce((acc, val) => (acc['"' + val.field + '"'] = val.value, acc), {});
 | 
				
			||||||
 | 
									var text = wrap({input: this.text || '\x01', width: 80, cut: true, untab: false, join: false});
 | 
				
			||||||
 | 
									for(var i = 0; i < text.length; ++i) res['"TEXT","' + (i + 1) + '","0"'] = text[i];
 | 
				
			||||||
 | 
									this.$emit('update', res);
 | 
				
			||||||
 | 
									if(await this.client.TIU_UPDATE_RECORD(this.ien, res, 0)) {
 | 
				
			||||||
 | 
										await this.client.TIU_GET_RECORD_TEXT_FLUSH(this.ien);
 | 
				
			||||||
 | 
										this.saved = new Date();
 | 
				
			||||||
 | 
										if(accept) this.$emit('accept');
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							created() {
 | 
				
			||||||
 | 
								this.resize = debounce(async function() {
 | 
				
			||||||
 | 
									var textarea = this.$refs.textarea;
 | 
				
			||||||
 | 
									textarea.style.height = 'auto';
 | 
				
			||||||
 | 
									await this.$nextTick();
 | 
				
			||||||
 | 
									textarea.style.height = textarea.scrollHeight + 4 + 'px';
 | 
				
			||||||
 | 
								}, 50);
 | 
				
			||||||
 | 
								this.autosave = debounce(this.update, 2000);
 | 
				
			||||||
 | 
								this.$watch(
 | 
				
			||||||
 | 
									() => (this.client, this.ien, {}),
 | 
				
			||||||
 | 
									async function() {
 | 
				
			||||||
 | 
										this.record = (this.client) && (this.ien) ? await this.client.TIU_LOAD_RECORD_FOR_EDIT(this.ien, '.01;.06;.07;1301;1204;1208;1701;1205;1405;2101;70201;70202') : [];
 | 
				
			||||||
 | 
										this.resize();
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									{ immediate: true }
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
							
								
								
									
										79
									
								
								htdocs/ViewDocNew.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								htdocs/ViewDocNew.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,79 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
						<div class="card mb-3 shadow">
 | 
				
			||||||
 | 
							<div class="card-header d-flex justify-content-between align-items-center">
 | 
				
			||||||
 | 
								<span>New document</span>
 | 
				
			||||||
 | 
								<a class="close" @click="() => $emit('cancel')">❌</a>
 | 
				
			||||||
 | 
							</div>
 | 
				
			||||||
 | 
							<ul class="list-group list-group-flush">
 | 
				
			||||||
 | 
								<li class="list-group-item"><ViewLocationLookup :client="client" :dfn="dfn" label="Visit" v-model="x_location" /></li>
 | 
				
			||||||
 | 
								<li class="list-group-item"><ViewDocTitleLookup :client="client" label="Title" v-model="x_title" /></li>
 | 
				
			||||||
 | 
								<li class="list-group-item"><ViewUserLookup :client="client" label="Author" v-model="x_author" /></li>
 | 
				
			||||||
 | 
								<li class="list-group-item"><DateTimePicker v-model="x_datetime" /></li>
 | 
				
			||||||
 | 
							</ul>
 | 
				
			||||||
 | 
							<div class="card-footer btn-group" role="group"><button class="btn btn-primary" :disabled="!((x_location) && (x_location.IEN) && (x_title) && (x_author))" @click="() => $emit('submit', { location: x_location, title: x_title, author: x_author, datetime: fmdatetime(x_datetime) })">Create</button></div>
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style scoped>
 | 
				
			||||||
 | 
						a.close {
 | 
				
			||||||
 | 
							cursor: default;
 | 
				
			||||||
 | 
							text-decoration: none;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script>
 | 
				
			||||||
 | 
						import { strptime, strftime } from './fmdatetime.mjs';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						import ViewLocationLookup from './ViewLocationLookup.vue';
 | 
				
			||||||
 | 
						import ViewDocTitleLookup from './ViewDocTitleLookup.vue';
 | 
				
			||||||
 | 
						import ViewUserLookup from './ViewUserLookup.vue';
 | 
				
			||||||
 | 
						import DateTimePicker from './DateTimePicker.vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						export default {
 | 
				
			||||||
 | 
							components: {
 | 
				
			||||||
 | 
								ViewLocationLookup, ViewDocTitleLookup, ViewUserLookup, DateTimePicker
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							props: {
 | 
				
			||||||
 | 
								client: Object,
 | 
				
			||||||
 | 
								dfn: String,
 | 
				
			||||||
 | 
								location: String,
 | 
				
			||||||
 | 
								title: String,
 | 
				
			||||||
 | 
								author: String,
 | 
				
			||||||
 | 
								datetime: {
 | 
				
			||||||
 | 
									type: String,
 | 
				
			||||||
 | 
									default: 'N'
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							emits: {
 | 
				
			||||||
 | 
								'cancel': null,
 | 
				
			||||||
 | 
								'submit': Object,
 | 
				
			||||||
 | 
								'update:location': String,
 | 
				
			||||||
 | 
								'update:title': String,
 | 
				
			||||||
 | 
								'update:author': String,
 | 
				
			||||||
 | 
								'update:datetime': String
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							data() {
 | 
				
			||||||
 | 
								return {
 | 
				
			||||||
 | 
									x_location: this.location,
 | 
				
			||||||
 | 
									x_title: this.title,
 | 
				
			||||||
 | 
									x_author: this.author,
 | 
				
			||||||
 | 
									x_datetime: this.datetime
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							watch: {
 | 
				
			||||||
 | 
								location(value) { this.x_location = value; },
 | 
				
			||||||
 | 
								x_location(value) { this.$emit('update:location', value); },
 | 
				
			||||||
 | 
								title(value) { this.x_title = value; },
 | 
				
			||||||
 | 
								x_title(value) { this.$emit('update:title', value); },
 | 
				
			||||||
 | 
								author(value) { this.x_author = value; },
 | 
				
			||||||
 | 
								x_author(value) { this.$emit('update:author', value); },
 | 
				
			||||||
 | 
								datetime(value) { this.x_datetime = value; },
 | 
				
			||||||
 | 
								x_datetime(value) { this.$emit('update:datetime', value); }
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							methods: {
 | 
				
			||||||
 | 
								fmdatetime(datetime) {
 | 
				
			||||||
 | 
									return strftime(strptime(datetime));
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
							
								
								
									
										123
									
								
								htdocs/ViewDocTitleLookup.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								htdocs/ViewDocTitleLookup.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,123 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
						<div v-if="label" class="input-group">
 | 
				
			||||||
 | 
							<span class="input-group-text">{{label}}</span>
 | 
				
			||||||
 | 
							<input class="form-control" placeholder="Filter..." v-model="x_query" />
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
						<input v-else class="form-control" placeholder="Filter..." v-model="x_query" />
 | 
				
			||||||
 | 
						<div class="scroller" ref="scroller">
 | 
				
			||||||
 | 
							<table class="table table-striped">
 | 
				
			||||||
 | 
								<tbody>
 | 
				
			||||||
 | 
									<tr v-for="item in resultset" :class="{ 'table-active': item.IEN == x_modelValue }" @click="x_modelValue = item.IEN">
 | 
				
			||||||
 | 
										<td>{{item.name}}</td>
 | 
				
			||||||
 | 
										<td style="text-align: right;">#{{item.IEN}}</td>
 | 
				
			||||||
 | 
									</tr>
 | 
				
			||||||
 | 
									<tr ref="bottom" />
 | 
				
			||||||
 | 
								</tbody>
 | 
				
			||||||
 | 
							</table>
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style scoped>
 | 
				
			||||||
 | 
						div.scroller {
 | 
				
			||||||
 | 
							max-height: 25vh;
 | 
				
			||||||
 | 
							overflow-y: auto;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						td {
 | 
				
			||||||
 | 
							cursor: default;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						.table-active, .table-active:nth-of-type(odd) > * {
 | 
				
			||||||
 | 
							color: #fff;
 | 
				
			||||||
 | 
							background-color: #0d6efd;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script>
 | 
				
			||||||
 | 
						import { debounce } from './util.mjs';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						export default {
 | 
				
			||||||
 | 
							props: {
 | 
				
			||||||
 | 
								client: Object,
 | 
				
			||||||
 | 
								label: String,
 | 
				
			||||||
 | 
								query: String,
 | 
				
			||||||
 | 
								modelValue: String
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							emits: {
 | 
				
			||||||
 | 
								'update:query': String,
 | 
				
			||||||
 | 
								'update:modelValue': String
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							data() {
 | 
				
			||||||
 | 
								return {
 | 
				
			||||||
 | 
									resultset: [],
 | 
				
			||||||
 | 
									has_more: false,
 | 
				
			||||||
 | 
									is_loading: true,
 | 
				
			||||||
 | 
									observer_bottom: null,
 | 
				
			||||||
 | 
									x_query: this.query,
 | 
				
			||||||
 | 
									x_modelValue: this.modelValue
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							computed: {
 | 
				
			||||||
 | 
								query_view() {
 | 
				
			||||||
 | 
									return this.x_query ? this.x_query.replace(/^\s+|\s+$/g, '').replace(/\s+/g, ' ').toUpperCase() : '';
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							watch: {
 | 
				
			||||||
 | 
								modelValue(value) { this.x_modelValue = value; },
 | 
				
			||||||
 | 
								x_modelValue(value) { this.$emit('update:modelValue', value); },
 | 
				
			||||||
 | 
								query(value) { this.x_query = value; },
 | 
				
			||||||
 | 
								x_query(value) { this.$emit('update:query', value); }
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							methods: {
 | 
				
			||||||
 | 
								async handle_bottom([entry]) {
 | 
				
			||||||
 | 
									if((entry.isIntersecting) && (this.has_more) && (!this.is_loading)) {
 | 
				
			||||||
 | 
										this.is_loading = true;
 | 
				
			||||||
 | 
										this.has_more = false;
 | 
				
			||||||
 | 
										try {
 | 
				
			||||||
 | 
											var batch = await this.client.TIU_LONG_LIST_OF_TITLES(3, this.resultset[this.resultset.length - 1].name, 1);
 | 
				
			||||||
 | 
											if(this.query_view.length >= 1) batch = batch.filter(x => x.name.startsWith(this.query_view));
 | 
				
			||||||
 | 
											if(batch.length > 0) {
 | 
				
			||||||
 | 
												Array.prototype.push.apply(this.resultset, batch);
 | 
				
			||||||
 | 
												this.has_more = true;
 | 
				
			||||||
 | 
											} else this.has_more = false;
 | 
				
			||||||
 | 
										} catch(ex) {
 | 
				
			||||||
 | 
											this.has_more = false;
 | 
				
			||||||
 | 
										} finally {
 | 
				
			||||||
 | 
											this.is_loading = false;
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							created() {
 | 
				
			||||||
 | 
								this.$watch(
 | 
				
			||||||
 | 
									() => (this.client, this.query_view, {}),
 | 
				
			||||||
 | 
									debounce(async function() {
 | 
				
			||||||
 | 
										if(this.client) {
 | 
				
			||||||
 | 
											this.is_loading = true;
 | 
				
			||||||
 | 
											this.has_more = false;
 | 
				
			||||||
 | 
											try {
 | 
				
			||||||
 | 
												var query = this.query_view;
 | 
				
			||||||
 | 
												if(query.length >= 1) {
 | 
				
			||||||
 | 
													var batch = await this.client.TIU_LONG_LIST_OF_TITLES(3, query.slice(0, -1) + String.fromCharCode(query.charCodeAt(query.length - 1) - 1) + '~', 1);
 | 
				
			||||||
 | 
													this.resultset = batch.filter(x => x.name.startsWith(query));
 | 
				
			||||||
 | 
												} else this.resultset = await this.client.TIU_LONG_LIST_OF_TITLES(3, '', 1);
 | 
				
			||||||
 | 
												this.has_more = this.resultset.length > 0;
 | 
				
			||||||
 | 
											} catch(ex) {
 | 
				
			||||||
 | 
												this.resultset = [];
 | 
				
			||||||
 | 
												this.has_more = false;
 | 
				
			||||||
 | 
											} finally {
 | 
				
			||||||
 | 
												this.is_loading = false;
 | 
				
			||||||
 | 
												if(this.$refs.scroller) this.$refs.scroller.scrollTo(0, 0);
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}, 500),
 | 
				
			||||||
 | 
									{ immediate: true }
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							mounted() {
 | 
				
			||||||
 | 
								this.observer_bottom = new IntersectionObserver(this.handle_bottom, { root: this.$refs.scroller, rootMargin: '25%' });
 | 
				
			||||||
 | 
								this.observer_bottom.observe(this.$refs.bottom);
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							destroyed() {
 | 
				
			||||||
 | 
								if(this.observer_bottom) this.observer_bottom.disconnect();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
							
								
								
									
										98
									
								
								htdocs/ViewDocView.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								htdocs/ViewDocView.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,98 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
						<ViewDocEdit v-if="(can_edit) && (is_editing)" :client="client" :dfn="dfn" :ien="ien" @update="x => $emit('update', x)" @accept="doc_accept" />
 | 
				
			||||||
 | 
						<div v-else class="card mb-3 shadow">
 | 
				
			||||||
 | 
							<div class="card-header d-flex justify-content-between align-items-center">
 | 
				
			||||||
 | 
								<span>{{localtitle || 'Document'}}</span>
 | 
				
			||||||
 | 
								<span>
 | 
				
			||||||
 | 
									<a v-if="can_delete" class="widget" @click="() => $emit('delete', ien)">🗑</a>
 | 
				
			||||||
 | 
									<a v-if="can_edit" class="widget" @click="() => is_editing = true">✏</a>
 | 
				
			||||||
 | 
									<a v-if="can_sign" class="widget" @click="() => $emit('sign', ien)">🔏</a>
 | 
				
			||||||
 | 
									<a class="widget" @click="() => $emit('cancel')">❌</a>
 | 
				
			||||||
 | 
								</span>
 | 
				
			||||||
 | 
							</div>
 | 
				
			||||||
 | 
							<div class="card-body">{{text}}</div>
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style scoped>
 | 
				
			||||||
 | 
						a.widget {
 | 
				
			||||||
 | 
							cursor: default;
 | 
				
			||||||
 | 
							text-decoration: none;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						div.card-body {
 | 
				
			||||||
 | 
							tab-size: 8;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script>
 | 
				
			||||||
 | 
						import ViewDocEdit from './ViewDocEdit.vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						export default {
 | 
				
			||||||
 | 
							components: {
 | 
				
			||||||
 | 
								ViewDocEdit
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							props: {
 | 
				
			||||||
 | 
								client: Object,
 | 
				
			||||||
 | 
								dfn: String,
 | 
				
			||||||
 | 
								ien: String,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							emits: {
 | 
				
			||||||
 | 
								'cancel': null,
 | 
				
			||||||
 | 
								'sign': null,
 | 
				
			||||||
 | 
								'update': Object,
 | 
				
			||||||
 | 
								'delete': String
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							data() {
 | 
				
			||||||
 | 
								return {
 | 
				
			||||||
 | 
									text: null,
 | 
				
			||||||
 | 
									can_sign: null,
 | 
				
			||||||
 | 
									can_edit: null,
 | 
				
			||||||
 | 
									can_delete: null
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							computed: {
 | 
				
			||||||
 | 
								is_editing: {
 | 
				
			||||||
 | 
									get() { return this.$route.query.hasOwnProperty('edit'); },
 | 
				
			||||||
 | 
									set(value) {
 | 
				
			||||||
 | 
										var query = { ...this.$route.query };
 | 
				
			||||||
 | 
										if(value) query.edit = '';
 | 
				
			||||||
 | 
										else delete query.edit;
 | 
				
			||||||
 | 
										this.$router.replace({ query });
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								localtitle() {
 | 
				
			||||||
 | 
									var doc = this.text;
 | 
				
			||||||
 | 
									if(doc) {
 | 
				
			||||||
 | 
										var brk = doc.indexOf('\r\n');
 | 
				
			||||||
 | 
										if(brk >= 0) {
 | 
				
			||||||
 | 
											doc = doc.substring(0, brk);
 | 
				
			||||||
 | 
											brk = doc.indexOf(': ');
 | 
				
			||||||
 | 
											if(brk >= 0) return doc.substring(brk + 2).replace(/^\s+|\s+$/g, '');
 | 
				
			||||||
 | 
											else return doc.replace(/^\s+|\s+$/g, '');
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							methods: {
 | 
				
			||||||
 | 
								async doc_accept() {
 | 
				
			||||||
 | 
									this.text = await this.client.TIU_GET_RECORD_TEXT(this.ien);
 | 
				
			||||||
 | 
									this.is_editing = false;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							created() {
 | 
				
			||||||
 | 
								this.$watch(
 | 
				
			||||||
 | 
									() => (this.client, this.ien, {}),
 | 
				
			||||||
 | 
									async function() {
 | 
				
			||||||
 | 
										this.text = this.can_edit = this.can_delete = null;
 | 
				
			||||||
 | 
										if((this.client) && (this.ien)) {
 | 
				
			||||||
 | 
											this.text = await this.client.TIU_GET_RECORD_TEXT(this.ien);
 | 
				
			||||||
 | 
											this.can_sign = (await this.client.TIU_AUTHORIZATION(this.ien, 'SIGNATURE') == '1') || (await this.client.TIU_AUTHORIZATION(this.ien, 'COSIGNATURE') == '1');
 | 
				
			||||||
 | 
											this.can_edit = await this.client.TIU_AUTHORIZATION(this.ien, 'EDIT RECORD') == '1';
 | 
				
			||||||
 | 
											this.can_delete = await this.client.TIU_AUTHORIZATION(this.ien, 'DELETE RECORD') == '1';
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									{ immediate: true }
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
@@ -40,7 +40,7 @@
 | 
				
			|||||||
		methods: {
 | 
							methods: {
 | 
				
			||||||
			async submit(payload) {
 | 
								async submit(payload) {
 | 
				
			||||||
				if((this.ien) && (this.dfn) && (payload)) {
 | 
									if((this.ien) && (this.dfn) && (payload)) {
 | 
				
			||||||
					var user_ien = (await this.client.userinfo()).result[0];
 | 
										var user_ien = (await this.client.authinfo()).duz;
 | 
				
			||||||
					var dgrp = await this.client.ORWDX_DGRP(this.dlgname.BaseDialogName);
 | 
										var dgrp = await this.client.ORWDX_DGRP(this.dlgname.BaseDialogName);
 | 
				
			||||||
					var res = await client.ORWDX_SAVE(this.dfn, 0/*user_ien*/, 0/*location_ien*/, this.dlgname.BaseDialogName, dgrp, this.dlgname.BaseDialogIEN, ''/*order_ifn*/, payload, '', '', '', 0);
 | 
										var res = await client.ORWDX_SAVE(this.dfn, 0/*user_ien*/, 0/*location_ien*/, this.dlgname.BaseDialogName, dgrp, this.dlgname.BaseDialogIEN, ''/*order_ifn*/, payload, '', '', '', 0);
 | 
				
			||||||
					console.log(res);
 | 
										console.log(res);
 | 
				
			||||||
@@ -52,8 +52,8 @@
 | 
				
			|||||||
				() => (this.client, this.ien, {}),
 | 
									() => (this.client, this.ien, {}),
 | 
				
			||||||
				async () => {
 | 
									async () => {
 | 
				
			||||||
					if((this.client) && (this.ien)) {
 | 
										if((this.client) && (this.ien)) {
 | 
				
			||||||
						var userinfo = await this.client.userinfo();
 | 
											var authinfo = await this.client.authinfo();
 | 
				
			||||||
						var user_ien = userinfo && userinfo.result ? userinfo.result[0] : '';
 | 
											var user_ien = authinfo && authinfo.success ? authinfo.duz : '';
 | 
				
			||||||
						this.dlgname = await this.client.ORWDXM_DLGNAME(this.ien);
 | 
											this.dlgname = await this.client.ORWDXM_DLGNAME(this.ien);
 | 
				
			||||||
						if(this.dlgname.BaseDialogIEN != this.ien) console.warn('IEN =', this.ien, '|', 'BaseDialogIEN =', this.dlgname.BaseDialogIEN);
 | 
											if(this.dlgname.BaseDialogIEN != this.ien) console.warn('IEN =', this.ien, '|', 'BaseDialogIEN =', this.dlgname.BaseDialogIEN);
 | 
				
			||||||
						this.dlgdef = await this.client.ORWDX_DLGDEF(this.dlgname.BaseDialogName);
 | 
											this.dlgdef = await this.client.ORWDX_DLGDEF(this.dlgname.BaseDialogName);
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										131
									
								
								htdocs/ViewUserLookup.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								htdocs/ViewUserLookup.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,131 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
						<div v-if="label" class="input-group">
 | 
				
			||||||
 | 
							<span class="input-group-text">{{label}}</span>
 | 
				
			||||||
 | 
							<input class="form-control" placeholder="Filter..." v-model="x_query" />
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
						<input v-else class="form-control" placeholder="Filter..." v-model="x_query" />
 | 
				
			||||||
 | 
						<div class="scroller" ref="scroller">
 | 
				
			||||||
 | 
							<table class="table table-striped">
 | 
				
			||||||
 | 
								<tbody>
 | 
				
			||||||
 | 
									<tr v-for="item in resultset" :class="{ 'table-active': item.DUZ == x_modelValue }" @click="x_modelValue = item.DUZ">
 | 
				
			||||||
 | 
										<td>{{item.name}}</td>
 | 
				
			||||||
 | 
										<td>{{item.description.replace(/^-\s*/g, '')}}</td>
 | 
				
			||||||
 | 
										<td style="text-align: right;">#{{item.DUZ}}</td>
 | 
				
			||||||
 | 
									</tr>
 | 
				
			||||||
 | 
									<tr ref="bottom" />
 | 
				
			||||||
 | 
								</tbody>
 | 
				
			||||||
 | 
							</table>
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style scoped>
 | 
				
			||||||
 | 
						div.scroller {
 | 
				
			||||||
 | 
							max-height: 25vh;
 | 
				
			||||||
 | 
							overflow-y: auto;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						td {
 | 
				
			||||||
 | 
							cursor: default;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						.table-active, .table-active:nth-of-type(odd) > * {
 | 
				
			||||||
 | 
							color: #fff;
 | 
				
			||||||
 | 
							background-color: #0d6efd;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script>
 | 
				
			||||||
 | 
						import { debounce } from './util.mjs';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						export default {
 | 
				
			||||||
 | 
							props: {
 | 
				
			||||||
 | 
								client: Object,
 | 
				
			||||||
 | 
								label: String,
 | 
				
			||||||
 | 
								query: String,
 | 
				
			||||||
 | 
								modelValue: String
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							emits: {
 | 
				
			||||||
 | 
								'update:query': String,
 | 
				
			||||||
 | 
								'update:modelValue': String
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							data() {
 | 
				
			||||||
 | 
								return {
 | 
				
			||||||
 | 
									resultset: [],
 | 
				
			||||||
 | 
									has_more: false,
 | 
				
			||||||
 | 
									is_loading: true,
 | 
				
			||||||
 | 
									observer_bottom: null,
 | 
				
			||||||
 | 
									x_query: this.query,
 | 
				
			||||||
 | 
									x_modelValue: this.modelValue
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							computed: {
 | 
				
			||||||
 | 
								query_view() {
 | 
				
			||||||
 | 
									return this.x_query ? this.x_query.replace(/^\s+|\s+$/g, '').replace(/\s+/g, ' ').toUpperCase() : '';
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							watch: {
 | 
				
			||||||
 | 
								modelValue(value) { this.x_modelValue = value; },
 | 
				
			||||||
 | 
								x_modelValue(value) { this.$emit('update:modelValue', value); },
 | 
				
			||||||
 | 
								query(value) { this.x_query = value; },
 | 
				
			||||||
 | 
								x_query(value) { this.$emit('update:query', value); }
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							methods: {
 | 
				
			||||||
 | 
								async set_default() {
 | 
				
			||||||
 | 
									if(this.x_modelValue) return;
 | 
				
			||||||
 | 
									var userinfo = await this.client.XUS_GET_USER_INFO();
 | 
				
			||||||
 | 
									this.x_modelValue = userinfo[0];
 | 
				
			||||||
 | 
									this.x_query = userinfo[1]
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								async handle_bottom([entry]) {
 | 
				
			||||||
 | 
									if((entry.isIntersecting) && (this.has_more) && (!this.is_loading)) {
 | 
				
			||||||
 | 
										this.is_loading = true;
 | 
				
			||||||
 | 
										this.has_more = false;
 | 
				
			||||||
 | 
										try {
 | 
				
			||||||
 | 
											var batch = await this.client.ORWU_NEWPERS(this.resultset[this.resultset.length - 1].name, 1, '', '', '', '', '', '', 0);
 | 
				
			||||||
 | 
											if(this.query_view.length >= 1) batch = batch.filter(x => x.name.toUpperCase().startsWith(this.query_view));
 | 
				
			||||||
 | 
											if(batch.length > 0) {
 | 
				
			||||||
 | 
												Array.prototype.push.apply(this.resultset, batch);
 | 
				
			||||||
 | 
												this.has_more = true;
 | 
				
			||||||
 | 
											} else this.has_more = false;
 | 
				
			||||||
 | 
										} catch(ex) {
 | 
				
			||||||
 | 
											this.has_more = false;
 | 
				
			||||||
 | 
										} finally {
 | 
				
			||||||
 | 
											this.is_loading = false;
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							created() {
 | 
				
			||||||
 | 
								this.$watch(
 | 
				
			||||||
 | 
									() => (this.client, this.query_view, {}),
 | 
				
			||||||
 | 
									debounce(async function() {
 | 
				
			||||||
 | 
										if(this.client) {
 | 
				
			||||||
 | 
											this.is_loading = true;
 | 
				
			||||||
 | 
											this.has_more = false;
 | 
				
			||||||
 | 
											try {
 | 
				
			||||||
 | 
												var query = this.query_view;
 | 
				
			||||||
 | 
												if(query.length >= 1) {
 | 
				
			||||||
 | 
													var batch = await this.client.ORWU_NEWPERS(query.slice(0, -1) + String.fromCharCode(query.charCodeAt(query.length - 1) - 1) + '~', 1, '', '', '', '', '', '', 0);
 | 
				
			||||||
 | 
													this.resultset = batch.filter(x => x.name.toUpperCase().startsWith(query));
 | 
				
			||||||
 | 
												} else this.resultset = await this.client.ORWU_NEWPERS('', 1, '', '', '', '', '', '', 0);
 | 
				
			||||||
 | 
												this.has_more = this.resultset.length > 0;
 | 
				
			||||||
 | 
											} catch(ex) {
 | 
				
			||||||
 | 
												this.resultset = [];
 | 
				
			||||||
 | 
												this.has_more = false;
 | 
				
			||||||
 | 
											} finally {
 | 
				
			||||||
 | 
												this.is_loading = false;
 | 
				
			||||||
 | 
												if(this.$refs.scroller) this.$refs.scroller.scrollTo(0, 0);
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}, 500),
 | 
				
			||||||
 | 
									{ immediate: true }
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
								this.set_default();
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							mounted() {
 | 
				
			||||||
 | 
								this.observer_bottom = new IntersectionObserver(this.handle_bottom, { root: this.$refs.scroller, rootMargin: '25%' });
 | 
				
			||||||
 | 
								this.observer_bottom.observe(this.$refs.bottom);
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							destroyed() {
 | 
				
			||||||
 | 
								if(this.observer_bottom) this.observer_bottom.disconnect();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
@@ -217,7 +217,7 @@ export function TplFS(client, parent, desc) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TplFS.fromUser = async function(client, user_ien=null) {
 | 
					TplFS.fromUser = async function(client, user_ien=null) {
 | 
				
			||||||
	if(!user_ien) user_ien = (await client.userinfo()).result[0];
 | 
						if(!user_ien) user_ien = (await client.authinfo()).duz;
 | 
				
			||||||
	return new TplFS(client, null, (await client.TIU_TEMPLATE_GETPROOT(user_ien))[0]);
 | 
						return new TplFS(client, null, (await client.TIU_TEMPLATE_GETPROOT(user_ien))[0]);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -30,8 +30,8 @@ export async function serverinfo(cid) {
 | 
				
			|||||||
	})).json();
 | 
						})).json();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function userinfo(cid) {
 | 
					export async function authinfo(cid) {
 | 
				
			||||||
	return await (await fetch('/v1/vista/' + cid + '/userinfo', {
 | 
						return await (await fetch('/v1/vista/' + cid + '/authinfo', {
 | 
				
			||||||
		method: 'POST',
 | 
							method: 'POST',
 | 
				
			||||||
		headers: { 'Content-Type': 'application/json' },
 | 
							headers: { 'Content-Type': 'application/json' },
 | 
				
			||||||
		body: '{}'
 | 
							body: '{}'
 | 
				
			||||||
@@ -47,5 +47,5 @@ export async function authenticate(cid, avcode=null) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default window.vista = {
 | 
					export default window.vista = {
 | 
				
			||||||
	connect, close, call, serverinfo, userinfo, authenticate
 | 
						connect, close, call, serverinfo, authinfo, authenticate
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -48,6 +48,8 @@ export const d_parse_boolean = data => data != '0';
 | 
				
			|||||||
export const d_parse_text = data => data !== '' ? data.join('\r\n') : data;
 | 
					export const d_parse_text = data => data !== '' ? data.join('\r\n') : data;
 | 
				
			||||||
export const d_parse_array = data => data !== '' ? data : [];
 | 
					export const d_parse_array = data => data !== '' ? data : [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const d_parse_authinfo = data => data ? { duz: data[0] != '0' ? data[0] : null, device_lock: data[1] != '0', change_verify: data[2] != '0', message: data[3], reserved: data[4], greeting_lines: data[5], greeting: data.slice(6), success: (data[0] != '0') && (data[2] == '0') } : { success: false }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const d_parse_orderdialogs = (data, columns=['IEN', 'windowFormId', 'displayGroupId', 'type', 'displayText']) => data.map(function(row) {
 | 
					export const d_parse_orderdialogs = (data, columns=['IEN', 'windowFormId', 'displayGroupId', 'type', 'displayText']) => data.map(function(row) {
 | 
				
			||||||
	row = row.split('^');
 | 
						row = row.split('^');
 | 
				
			||||||
	row = [...row[0].split(';'), row[1]];
 | 
						row = [...row[0].split(';'), row[1]];
 | 
				
			||||||
@@ -187,6 +189,20 @@ export const d_parse_multireport = data => {
 | 
				
			|||||||
	return (res._ts = _ts, res);
 | 
						return (res._ts = _ts, res);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const d_parse_tiurecordtiux = data => {
 | 
				
			||||||
 | 
						var res = {};
 | 
				
			||||||
 | 
						if(data.length < 1) return res;
 | 
				
			||||||
 | 
						var brk = data.indexOf('$TXT'), text = undefined;
 | 
				
			||||||
 | 
						if(brk >= 0) {
 | 
				
			||||||
 | 
							text = data.slice(brk + 1).join('\r\n');
 | 
				
			||||||
 | 
							data = data.slice(0, brk);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						data = d_split(data, '^', 'field', 'value', 'description');
 | 
				
			||||||
 | 
						data = data.reduce((acc, val) => (acc['~' + val.field] = val, acc), data);
 | 
				
			||||||
 | 
						if(text) data.text = text;
 | 
				
			||||||
 | 
						return data;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const d_parse_tiudocumentlist = data => d_split(data, '^', 'IEN', 'title', 'time', 'patient', 'author', 'location', 'status', 'visit').map(row => {
 | 
					export const d_parse_tiudocumentlist = data => d_split(data, '^', 'IEN', 'title', 'time', 'patient', 'author', 'location', 'status', 'visit').map(row => {
 | 
				
			||||||
	row.author = row.author ? d_split1(row.author, ';', 'IEN', 'byline', 'name') : null;
 | 
						row.author = row.author ? d_split1(row.author, ';', 'IEN', 'byline', 'name') : null;
 | 
				
			||||||
	row.visit = row.visit ? d_split1(row.visit, ';', 'date', 'time') : null;
 | 
						row.visit = row.visit ? d_split1(row.visit, ';', 'date', 'time') : null;
 | 
				
			||||||
@@ -240,11 +256,11 @@ export function Client(cid, secret) {
 | 
				
			|||||||
		return heartbeat = window.setInterval(this.XWB_IM_HERE, interval);
 | 
							return heartbeat = window.setInterval(this.XWB_IM_HERE, interval);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	this.serverinfo = () => vista.serverinfo(cid);
 | 
						this.serverinfo = () => vista.serverinfo(cid);
 | 
				
			||||||
	this.userinfo = () => vista.userinfo(cid);
 | 
						this.authinfo = aflow(() => vista.authinfo(cid), d_unwrap, d_parse_authinfo);
 | 
				
			||||||
	this.authenticate = (avcode=null) => vista.authenticate(cid, avcode);
 | 
						this.authenticate = aflow((avcode=null) => vista.authenticate(cid, avcode), d_unwrap, d_parse_authinfo);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if(!localstate.encfs) localstate.encfs = tplfs_randpassword();
 | 
						if(!localstate.encfs) localstate.encfs = tplfs_randpassword();
 | 
				
			||||||
	this.tplfs = async () => this._tplfs ? this._tplfs : (this._tplfs = await TplFS.fromUser(this, (await this.userinfo()).result[0]));
 | 
						this.tplfs = async () => this._tplfs ? this._tplfs : (this._tplfs = await TplFS.fromUser(this, (await this.authinfo()).duz));
 | 
				
			||||||
	this.encfs = async () => this._encfs ? this._encfs : (this._encfs = await EncFS.fromPassword(await this.tplfs(), localstate.encfs));
 | 
						this.encfs = async () => this._encfs ? this._encfs : (this._encfs = await EncFS.fromPassword(await this.tplfs(), localstate.encfs));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	this.remotestate = reactive({});
 | 
						this.remotestate = reactive({});
 | 
				
			||||||
@@ -309,10 +325,24 @@ export function Client(cid, secret) {
 | 
				
			|||||||
	this.TIU_TEMPLATE_LOCK = aflow((...args) => this.call({ method: 'TIU_TEMPLATE_LOCK', context: ['OR CPRS GUI CHART'], ttl: 0, stale: false }, ...args), d_log, d_unwrap);
 | 
						this.TIU_TEMPLATE_LOCK = aflow((...args) => this.call({ method: 'TIU_TEMPLATE_LOCK', context: ['OR CPRS GUI CHART'], ttl: 0, stale: false }, ...args), d_log, d_unwrap);
 | 
				
			||||||
	this.TIU_TEMPLATE_UNLOCK = aflow((...args) => this.call({ method: 'TIU_TEMPLATE_UNLOCK', context: ['OR CPRS GUI CHART'], ttl: 0, stale: false }, ...args), d_log, d_unwrap);
 | 
						this.TIU_TEMPLATE_UNLOCK = aflow((...args) => this.call({ method: 'TIU_TEMPLATE_UNLOCK', context: ['OR CPRS GUI CHART'], ttl: 0, stale: false }, ...args), d_log, d_unwrap);
 | 
				
			||||||
	this.TIU_DOCUMENTS_BY_CONTEXT = aflow((...args) => this.call({ method: 'TIU_DOCUMENTS_BY_CONTEXT', context: ['OR CPRS GUI CHART'], ttl: 60, stale: false }, ...args), d_log, d_unwrap, d_parse_array, d_parse_tiudocumentlist);
 | 
						this.TIU_DOCUMENTS_BY_CONTEXT = aflow((...args) => this.call({ method: 'TIU_DOCUMENTS_BY_CONTEXT', context: ['OR CPRS GUI CHART'], ttl: 60, stale: false }, ...args), d_log, d_unwrap, d_parse_array, d_parse_tiudocumentlist);
 | 
				
			||||||
 | 
						this.TIU_DOCUMENTS_BY_CONTEXT_FLUSH = aflow((...args) => this.call({ method: 'TIU_DOCUMENTS_BY_CONTEXT', context: ['OR CPRS GUI CHART'], ttl: 0, stale: false }, ...args), d_log, d_unwrap, d_parse_array, d_parse_tiudocumentlist);
 | 
				
			||||||
	this.TIU_GET_RECORD_TEXT = aflow((...args) => this.call({ method: 'TIU_GET_RECORD_TEXT', context: ['OR CPRS GUI CHART'], ttl: 60, stale: false }, ...args), d_log, d_unwrap, d_parse_text);
 | 
						this.TIU_GET_RECORD_TEXT = aflow((...args) => this.call({ method: 'TIU_GET_RECORD_TEXT', context: ['OR CPRS GUI CHART'], ttl: 60, stale: false }, ...args), d_log, d_unwrap, d_parse_text);
 | 
				
			||||||
 | 
						this.TIU_GET_RECORD_TEXT_FLUSH = aflow((...args) => this.call({ method: 'TIU_GET_RECORD_TEXT', context: ['OR CPRS GUI CHART'], ttl: 0, stale: false }, ...args), d_log, d_unwrap, d_parse_text);
 | 
				
			||||||
 | 
						this.TIU_LONG_LIST_OF_TITLES = memoized(aflow((...args) => this.call({ method: 'TIU_LONG_LIST_OF_TITLES', context: ['OR CPRS GUI CHART'], ttl: 86400, stale: true, ttl: 86400, stale: true }, ...args), d_log, d_unwrap, f_split('^', 'IEN', 'name')));
 | 
				
			||||||
 | 
						this.TIU_CREATE_RECORD = aflow((...args) => this.call({ method: 'TIU_CREATE_RECORD', context: ['OR CPRS GUI CHART'], ttl: 0, stale: false }, ...args), d_log, d_unwrap);
 | 
				
			||||||
 | 
						this.TIU_AUTHORIZATION = aflow((...args) => this.call({ method: 'TIU_AUTHORIZATION', context: ['OR CPRS GUI CHART'], ttl: 0, stale: false }, ...args), d_log, d_unwrap);
 | 
				
			||||||
 | 
						this.TIU_LOAD_RECORD_FOR_EDIT = aflow((...args) => this.call({ method: 'TIU_LOAD_RECORD_FOR_EDIT', context: ['OR CPRS GUI CHART'], ttl: 0, stale: false }, ...args), d_log, d_unwrap, d_parse_tiurecordtiux);
 | 
				
			||||||
 | 
						this.TIU_UPDATE_RECORD = aflow((...args) => this.call({ method: 'TIU_UPDATE_RECORD', context: ['OR CPRS GUI CHART'], ttl: 0, stale: false }, ...args), d_log, d_unwrap);
 | 
				
			||||||
 | 
						this.TIU_DELETE_RECORD = aflow((...args) => this.call({ method: 'TIU_DELETE_RECORD', context: ['OR CPRS GUI CHART'], ttl: 0, stale: false }, ...args), d_log, d_unwrap);
 | 
				
			||||||
 | 
						this.TIU_SIGN_RECORD = aflow((...args) => this.call({ method: 'TIU_SIGN_RECORD', context: ['OR CPRS GUI CHART'], ttl: 0, stale: false }, ...args), d_log, d_unwrap);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						this.ORWPCE_NOTEVSTR = aflow((...args) => this.call({ method: 'ORWPCE_NOTEVSTR', context: ['OR CPRS GUI CHART'], ttl: 86400, stale: true }, ...args), d_log, d_unwrap);
 | 
				
			||||||
 | 
						this.ORWPCE_DELETE = aflow((...args) => this.call({ method: 'ORWPCE_DELETE', context: ['OR CPRS GUI CHART'], ttl: 0, stale: false }, ...args), d_log, d_unwrap);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	this.ORWCV_VST = memoized(aflow((...args) => this.callctx(['OR CPRS GUI CHART'], 'ORWCV_VST', ...args), d_log, d_unwrap, f_split('^', 'apptinfo', 'datetime', 'location', 'status')));
 | 
						this.ORWCV_VST = memoized(aflow((...args) => this.callctx(['OR CPRS GUI CHART'], 'ORWCV_VST', ...args), d_log, d_unwrap, f_split('^', 'apptinfo', 'datetime', 'location', 'status')));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						this.ORWU_NEWPERS = memoized(aflow((...args) => this.call({ method: 'ORWU_NEWPERS', context: ['OR CPRS GUI CHART'], ttl: 86400, stale: true }, ...args), d_log, d_unwrap, f_split('^', 'DUZ', 'name', 'description')));
 | 
				
			||||||
 | 
						this.ORWU_VALIDSIG = memoized(aflow((...args) => this.call({ method: 'ORWU_VALIDSIG', context: ['OR CPRS GUI CHART'], ttl: 0, stale: false }, ...args), d_log, d_unwrap));
 | 
				
			||||||
	this.ORWU1_NEWLOC = memoized(aflow((...args) => this.callctx(['OR CPRS GUI CHART'], 'ORWU1_NEWLOC', ...args), d_log, d_unwrap, f_split('^', 'IEN', 'name')));
 | 
						this.ORWU1_NEWLOC = memoized(aflow((...args) => this.callctx(['OR CPRS GUI CHART'], 'ORWU1_NEWLOC', ...args), d_log, d_unwrap, f_split('^', 'IEN', 'name')));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	this.ORWDX_DGNM = memoized(aflow((...args) => this.callctx(['OR CPRS GUI CHART'], 'ORWDX_DGNM', ...args), d_log, d_unwrap));
 | 
						this.ORWDX_DGNM = memoized(aflow((...args) => this.callctx(['OR CPRS GUI CHART'], 'ORWDX_DGNM', ...args), d_log, d_unwrap));
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										6
									
								
								main.py
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								main.py
									
									
									
									
									
								
							@@ -92,11 +92,11 @@ def application():
 | 
				
			|||||||
			logger.exception(request.url)
 | 
								logger.exception(request.url)
 | 
				
			||||||
			return jsonify_error(ex, id=request.json.get('id'))
 | 
								return jsonify_error(ex, id=request.json.get('id'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@app.post('/v1/vista/<cid>/userinfo')
 | 
						@app.post('/v1/vista/<cid>/authinfo')
 | 
				
			||||||
	def cb_userinfo(cid):
 | 
						def cb_authinfo(cid):
 | 
				
			||||||
		try:
 | 
							try:
 | 
				
			||||||
			client = clients[cid]
 | 
								client = clients[cid]
 | 
				
			||||||
			return jsonify_result(client._obj._user, id=request.json.get('id'))
 | 
								return jsonify_result(client._obj._auth, id=request.json.get('id'))
 | 
				
			||||||
		except Exception as ex:
 | 
							except Exception as ex:
 | 
				
			||||||
			logger.exception(request.url)
 | 
								logger.exception(request.url)
 | 
				
			||||||
			return jsonify_error(ex, id=request.json.get('id'))
 | 
								return jsonify_error(ex, id=request.json.get('id'))
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										16
									
								
								rpc.py
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								rpc.py
									
									
									
									
									
								
							@@ -112,7 +112,7 @@ class ClientSync(object):
 | 
				
			|||||||
		self.recv_rpc_msg = recv_rpc_msg(self.sock)
 | 
							self.recv_rpc_msg = recv_rpc_msg(self.sock)
 | 
				
			||||||
		self.lock = threading.Lock()
 | 
							self.lock = threading.Lock()
 | 
				
			||||||
		self._server = { 'host': host, 'port': port }
 | 
							self._server = { 'host': host, 'port': port }
 | 
				
			||||||
		self._user = None
 | 
							self._auth = None
 | 
				
			||||||
		self.context = 'XUS SIGNON'
 | 
							self.context = 'XUS SIGNON'
 | 
				
			||||||
		if TCPConnect and (res := self.TCPConnect(self.sock.getsockname()[0], '0', socket.gethostname())) != 'accept':
 | 
							if TCPConnect and (res := self.TCPConnect(self.sock.getsockname()[0], '0', socket.gethostname())) != 'accept':
 | 
				
			||||||
			raise RPCExcInvalidResult('TCPConnect', self.sock.getsockname()[0], '0', socket.gethostname(), res)
 | 
								raise RPCExcInvalidResult('TCPConnect', self.sock.getsockname()[0], '0', socket.gethostname(), res)
 | 
				
			||||||
@@ -144,13 +144,11 @@ class ClientSync(object):
 | 
				
			|||||||
		return res
 | 
							return res
 | 
				
			||||||
	def authenticate(self, identity: str, *, context=('XUS SIGNON',)):
 | 
						def authenticate(self, identity: str, *, context=('XUS SIGNON',)):
 | 
				
			||||||
		self._server.update(RecordServerInfo(*self('XUS SIGNON SETUP', '', '1', context=context))._asdict())
 | 
							self._server.update(RecordServerInfo(*self('XUS SIGNON SETUP', '', '1', context=context))._asdict())
 | 
				
			||||||
		if identity.startswith('<?xml version="1.0" encoding="UTF-8"?>'):
 | 
							if identity.startswith('<?xml version="1.0"'):
 | 
				
			||||||
			res = self('XUS ESSO VALIDATE', RPCType(tuple(identity[i:i+200] for i in range(0, len(identity), 200)), RPCType.GLOBAL))
 | 
								res = self('XUS ESSO VALIDATE', RPCType(tuple(identity[i:i+200] for i in range(0, len(identity), 200)), RPCType.GLOBAL))
 | 
				
			||||||
		else:
 | 
							else:
 | 
				
			||||||
			res = self('XUS AV CODE', XWBHash_encrypt(identity))
 | 
								res = self('XUS AV CODE', XWBHash_encrypt(identity))
 | 
				
			||||||
		if res[0] == '0' or res[2] != '0':
 | 
							self._auth = res if res[0] != '0' else None
 | 
				
			||||||
			raise RPCExcAuth(res[3], res)
 | 
					 | 
				
			||||||
		self._user = res
 | 
					 | 
				
			||||||
		return res
 | 
							return res
 | 
				
			||||||
	def keepalive(self, interval=None, *, context=('XUS SIGNON',)):
 | 
						def keepalive(self, interval=None, *, context=('XUS SIGNON',)):
 | 
				
			||||||
		import time
 | 
							import time
 | 
				
			||||||
@@ -188,7 +186,7 @@ class ClientAsync(object):
 | 
				
			|||||||
		self.arecv_rpc_msg = arecv_rpc_msg(self.reader)
 | 
							self.arecv_rpc_msg = arecv_rpc_msg(self.reader)
 | 
				
			||||||
		self.lock = asyncio.Lock()
 | 
							self.lock = asyncio.Lock()
 | 
				
			||||||
		self._server = { 'host': host, 'port': port, 'info': None }
 | 
							self._server = { 'host': host, 'port': port, 'info': None }
 | 
				
			||||||
		self._user = None
 | 
							self._auth = None
 | 
				
			||||||
		self.context = 'XUS SIGNON'
 | 
							self.context = 'XUS SIGNON'
 | 
				
			||||||
		if TCPConnect and (res := await self.TCPConnect(self.writer.get_extra_info('sockname')[0], '0', socket.gethostname())) != 'accept':
 | 
							if TCPConnect and (res := await self.TCPConnect(self.writer.get_extra_info('sockname')[0], '0', socket.gethostname())) != 'accept':
 | 
				
			||||||
			raise RPCExcInvalidResult('TCPConnect', self.writer.get_extra_info('sockname')[0], '0', socket.gethostname(), res)
 | 
								raise RPCExcInvalidResult('TCPConnect', self.writer.get_extra_info('sockname')[0], '0', socket.gethostname(), res)
 | 
				
			||||||
@@ -225,13 +223,11 @@ class ClientAsync(object):
 | 
				
			|||||||
		return res
 | 
							return res
 | 
				
			||||||
	async def authenticate(self, identity: str, *, context=('XUS SIGNON',)):
 | 
						async def authenticate(self, identity: str, *, context=('XUS SIGNON',)):
 | 
				
			||||||
		self._server.update(RecordServerInfo(*await self('XUS SIGNON SETUP', '', '1', context=context))._asdict())
 | 
							self._server.update(RecordServerInfo(*await self('XUS SIGNON SETUP', '', '1', context=context))._asdict())
 | 
				
			||||||
		if identity.startswith('<?xml version="1.0" encoding="UTF-8"?>'):
 | 
							if identity.startswith('<?xml version="1.0"'):
 | 
				
			||||||
			res = await self('XUS ESSO VALIDATE', RPCType(tuple(identity[i:i+200] for i in range(0, len(identity), 200)), RPCType.GLOBAL))
 | 
								res = await self('XUS ESSO VALIDATE', RPCType(tuple(identity[i:i+200] for i in range(0, len(identity), 200)), RPCType.GLOBAL))
 | 
				
			||||||
		else:
 | 
							else:
 | 
				
			||||||
			res = await self('XUS AV CODE', XWBHash_encrypt(identity))
 | 
								res = await self('XUS AV CODE', XWBHash_encrypt(identity))
 | 
				
			||||||
		if res[0] == '0' or res[2] != '0':
 | 
							self._auth = res if res[0] != '0' else None
 | 
				
			||||||
			raise RPCExcAuth(res[3], res)
 | 
					 | 
				
			||||||
		self._user = res
 | 
					 | 
				
			||||||
		return res
 | 
							return res
 | 
				
			||||||
	async def keepalive(self, interval=None, *, context=('XUS SIGNON',)):
 | 
						async def keepalive(self, interval=None, *, context=('XUS SIGNON',)):
 | 
				
			||||||
		interval = interval or 0.45*float((await self.XWB_GET_BROKER_INFO(context=context))[0])
 | 
							interval = interval or 0.45*float((await self.XWB_GET_BROKER_INFO(context=context))[0])
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user