source: extensions/net.sf.basedb.reggie/trunk/resources/persinfo.jsp @ 1305

Last change on this file since 1305 was 1305, checked in by Nicklas Nordborg, 11 years ago

References #291: Personal information registration

Move to next input field when ENTER is pressed.

File size: 19.6 KB
Line 
1<%@ page
2  pageEncoding="UTF-8"
3  session="false"
4  import="net.sf.basedb.core.Application"
5  import="net.sf.basedb.core.User"
6  import="net.sf.basedb.core.DbControl"
7  import="net.sf.basedb.core.SessionControl"
8  import="net.sf.basedb.clients.web.Base"
9  import="net.sf.basedb.clients.web.util.HTML"
10  import="net.sf.basedb.util.Values"
11%>
12<%@ taglib prefix="base" uri="/WEB-INF/base.tld" %>
13<%@ taglib prefix="p" uri="/WEB-INF/path.tld" %>
14<%
15final SessionControl sc = Base.getExistingSessionControl(request, true);
16final String ID = sc.getId();
17final float scale = Base.getScale(sc);
18DbControl dc = null;
19try
20{
21  dc = sc.newDbControl();
22  final User user = User.getById(dc, sc.getLoggedInUserId());
23%>
24<base:page type="default" >
25<base:head scripts="ajax.js" styles="path.css">
26<script language="JavaScript">
27
28var currentStep = 1;
29var pnrIsValid = false;
30var caseIsValid = false;
31var lateralityIsValid = false;
32
33var patientInfo = null;
34var caseInfo = null;
35
36function init()
37{
38  var frm = document.forms['reggie'];
39  frm.caseName.focus();
40}
41
42function step1IsValid()
43{
44  return pnrIsValid && caseIsValid;
45}
46
47function step2IsValid()
48{
49  var formOk = true;
50  var frm = document.forms['reggie'];
51
52  // New patient only
53  if (!patientInfo.id)
54  {
55    // Validate 'New patient' form
56    if (frm.allFirstNames.value == '')
57    {
58      setInputStatus('allFirstNames', 'Missing', 'invalid');
59      frm.allFirstNames.focus();
60      formOk = false;
61    }
62    else
63    {
64      setInputStatus('allFirstNames', '', 'valid');
65    }
66   
67    if (frm.familyName.value == '')
68    {
69      setInputStatus('familyName', 'Missing', 'invalid');
70      frm.familyName.focus();
71      formOk = false;
72    }
73    else
74    {
75      setInputStatus('familyName', '', 'valid');
76    }
77
78    if (frm.patientCode.value == '')
79    {
80      setInputStatus('patientCode', 'Missing', 'invalid');
81      frm.patientCode.focus();
82      formOk = false;
83    }
84    else
85    {
86      setInputStatus('patientCode', '', 'valid');
87    }
88  }
89  return formOk;
90}
91
92function step3IsValid()
93{
94  return lateralityIsValid;
95}
96
97function goNext()
98{
99  if (currentStep == 1)
100  {
101    if (step1IsValid()) gotoStep2();
102  }
103  else if (currentStep == 2)
104  {
105    if (step2IsValid()) gotoStep3();
106  }
107}
108
109function caseNameOnChange()
110{
111  var frm = document.forms['reggie'];
112  var caseName = frm.caseName.value;
113  if (caseName == '')
114  {
115    setInputStatus('case', 'Missing', 'invalid');
116    return;
117  }
118  setInputStatus('case', '', 'valid');
119  caseIsValid = true;
120}
121
122function personalNumberOnChange()
123{
124  var frm = document.forms['reggie'];
125  var pnr = frm.personalNumber.value;
126  pnrIsValid = false;
127 
128  if (pnr.length < 12)
129  {
130    setInputStatus('pnr', 'Too short', 'invalid');
131    return;
132  }
133 
134  if (!Dates.isDate(pnr.substring(0, 8), 'yyyyMMdd'))
135  {
136    setInputStatus('pnr', 'Not a valid date', 'invalid');
137    return;
138  }
139 
140  var sum = 0;
141  var factor = 2;
142  var x = '';
143  for (var i = 2; i < 11; i++)
144  {
145    var digit = parseInt(pnr.substr(i, 1));
146    var tmp = factor * digit;
147    x += '(' + factor + '*' + digit+')';
148    sum += tmp >= 10 ? tmp - 9: tmp;
149    factor = factor == 2 ? 1 : 2;
150  }
151 
152  var control = 10 - (sum % 10);
153  if (control != parseInt(pnr.substr(11, 1)))
154  {
155    setInputStatus('pnr', 'Invalid control digit', 'invalid');
156    return;
157  }
158 
159  setInputStatus('pnr', '', 'valid');
160  pnrIsValid = true;
161}
162
163function lateralityOnChange()
164{
165  lateralityIsValid = false;
166 
167  // Check selected laterality against specimen tubes
168  var frm = document.forms['reggie'];
169  var selectedLaterality = Forms.getCheckedRadio(frm.laterality);
170 
171  // No laterality/case selected
172  if (selectedLaterality == null)
173  {
174    setInputStatus('laterality', 'Not selected', 'invalid');
175    return;
176  }
177  setInputStatus('laterality', '', 'valid');
178  lateralityIsValid = true;
179 
180  var laterality = selectedLaterality.value;
181  if (laterality != 'LEFT' && laterality != 'RIGHT')
182  {
183    // It is the ID of an existing case
184    for (var i = 0; i < patientInfo.cases.length; i++)
185    {
186      var cse = patientInfo.cases[i];
187      if (cse.id == laterality)
188      {
189        laterality = cse.laterality;
190        break;
191      }
192    }
193  }
194 
195  if (!laterality) return;
196 
197  // No specimen tubes
198  if (!caseInfo.specimen || caseInfo.specimen.length == 0) return;
199  for (var i = 0; i < caseInfo.specimen.length; i++)
200  {
201    var specimen = caseInfo.specimen[i];
202    if (specimen.laterality && specimen.laterality != laterality)
203    {
204      setInputStatus('laterality', 'Not same laterality as specimen tubes', 'warning');
205      return;
206    }
207  }
208}
209
210function goNextOnTab(event)
211{
212  if (event.keyCode == 9) setTimeout('goNext()', 200);
213  return true;
214}
215
216function goNextOnTabOrEnter(event)
217{
218  if (event.keyCode == 9 || event.keyCode == 13) setTimeout('goNext()', 200);
219  return true;
220}
221
222function focusOnEnter(event, inputField)
223{
224  if (event.keyCode == 13) setTimeout("document.forms['reggie']."+inputField+".focus()", 200);
225  return true;
226}
227
228function gotoStep2()
229{
230  // Check entered case and pnr with AJAX
231  var frm = document.forms['reggie'];
232  frm.caseName.disabled = true;
233  frm.personalNumber.disabled = true;
234  currentStep = 2;
235 
236  var pnr = frm.personalNumber.value;
237  var caseName = frm.caseName.value;
238  var request = Ajax.getXmlHttpRequest();
239  var url = 'PersonalRegistration.servlet?ID=<%=ID%>&cmd=CheckPersonalNumberAndCaseName';
240  url += '&personalNumber=' + pnr;
241  url += '&caseName=' + caseName;
242  request.open("GET", url, false);
243  request.send(null);
244 
245  //setInnerHTML('debug', request.responseText);
246 
247  var response = JSON.parse(request.responseText);
248  if (response.status != 'ok')
249  {
250    setFatalError(response.message);
251    return false;
252  }
253 
254  // Get biosource information from the AJAX response
255  patientInfo = response.patientInfo;
256  caseInfo = response.caseInfo;
257 
258  if (!patientInfo.id)
259  {
260    Main.show('newPatientSection');
261    frm.patientCode.value=patientInfo.name;
262    setInnerHTML('new.dateOfBirth', patientInfo.dateOfBirth);
263    setInnerHTML('new.gender', patientInfo.gender);
264    frm.familyName.focus();
265  }
266  else
267  {
268    Main.show('existingPatientSection');
269    setInnerHTML('existing.patientCode', patientInfo.name);
270    setInnerHTML('existing.familyName', patientInfo.familyName);
271    setInnerHTML('existing.allFirstNames', patientInfo.allFirstNames);
272    setInnerHTML('existing.dateOfBirth', patientInfo.dateOfBirth);
273    setInnerHTML('existing.gender', patientInfo.gender);
274    gotoStep3();
275  }
276}
277
278function gotoStep3()
279{
280  // Check entered case and pnr with AJAX
281  var frm = document.forms['reggie'];
282 
283  if (!patientInfo.id)
284  {
285    frm.patientCode.disabled = true;
286    frm.familyName.disabled = true;
287    frm.allFirstNames.disabled = true;
288    patientInfo.familyName = frm.familyName.value;
289    patientInfo.allFirstNames = frm.allFirstNames.value;
290  }
291  currentStep = 3;
292 
293  // Generate list of specimen tubes
294  var thisCaseLaterality;
295  if (caseInfo.specimen && caseInfo.specimen.length > 0)
296  {
297    var specimenTubes = '';
298    Main.hide('reasonIfNoSpecimenSection');
299    for (var i = 0; i < caseInfo.specimen.length; i++)
300    {
301      var specimen = caseInfo.specimen[i];
302      specimenTubes += specimen.name ;
303      if (specimen.laterality) 
304      {
305        specimenTubes += ' ('+specimen.laterality + ')';
306        Forms.checkRadio(frm.laterality, specimen.laterality);
307       
308        if (thisCaseLaterality && thisCaseLaterality != specimen.laterality)
309        {
310          setInputStatus('specimenTubes', 'Specimen tubes with different laterality', 'warning');
311        }
312        thisCaseLaterality = specimen.laterality;
313      }
314      else
315      {
316        specimenTubes += ' (<i>unknown laterality</i>)';
317      }
318      specimenTubes += '<br>';
319    }
320    setInnerHTML('specimenTubes', specimenTubes);
321  }
322
323 
324  Main.show('caseSection');
325 
326  // Existing cases for this patient
327  var hasLeftCase = false;
328  var hasRightCase = false;
329  if (patientInfo.cases && patientInfo.cases.length > 0)
330  {
331    var cases = '';
332    for (var i = 0; i < patientInfo.cases.length; i++)
333    {
334      var cc = patientInfo.cases[i];
335      cases += '<input type="radio" name="laterality" value="' + cc.id + '"';
336      if (cc.laterality == 'LEFT') 
337      {
338        if (hasLeftCase) setInputStatus('laterality', 'Two cases with LEFT', 'warning');
339        hasLeftCase = true;
340      }
341      if (cc.laterality == 'RIGHT') 
342      {
343        if (hasRightCase) setInputStatus('laterality', 'Two cases with RIGHT', 'warning');
344        hasRightCase = true;
345      }
346      if (cc.laterality == thisCaseLaterality) cases += ' checked';
347      cases += ' onclick="lateralityOnChange()">' + cc.name;
348      if (cc.laterality)
349      {
350        cases += ' (' + cc.laterality + ')<br>';
351      }
352      else
353      {
354        cases += ' (<i>unknown laterality</i>)<br>';
355      }
356    }
357   
358    if (patientInfo.cases.length == 1)
359    {
360      if (!hasLeftCase)
361      {
362        cases += '<input type="radio" name="laterality" value="LEFT" ';
363        if (thisCaseLaterality == 'LEFT') cases += ' checked';
364        cases += ' onclick="lateralityOnChange()"><i>new case</i> (LEFT)<br>';
365      }
366      if (!hasRightCase)
367      {
368        cases += '<input type="radio" name="laterality" value="RIGHT"';
369        if (thisCaseLaterality == 'RIGHT') cases += ' checked';
370        cases += ' onclick="lateralityOnChange()"><i>new case</i> (RIGHT)<br>';
371      }
372    }
373   
374    if (patientInfo.cases.length == 2)
375    {
376      setInnerHTML('laterality.prompt', 'Merge with case');
377    }
378    else
379    {
380      setInnerHTML('laterality.prompt', 'Merge/create new case');
381    }
382   
383    setInnerHTML('laterality.input', cases);
384  }
385  lateralityOnChange();
386
387  Main.hide('gonext');
388  Main.show('gocreate');
389}
390
391function goCreate()
392{
393  if (!step3IsValid()) return;
394 
395  Main.hide('gocreate');
396  var frm = document.forms['reggie'];
397
398  caseInfo.laterality = Forms.getCheckedRadio(frm.laterality).value;
399  caseInfo.reasonIfNoSpecimen = frm.reasonIfNoSpecimen.value;
400
401  for (var i = 0; i < frm.laterality.length; i++)
402  {
403    frm.laterality[i].disabled = true;
404  }
405  frm.reasonIfNoSpecimen.disabled = true;
406
407  var submitInfo = new Object();
408  submitInfo.patientInfo = patientInfo;
409  submitInfo.caseInfo = caseInfo;
410
411  var request = Ajax.getXmlHttpRequest();
412  var url = 'PersonalRegistration.servlet?ID=<%=ID%>&cmd=Create';
413  request.open("POST", url, false);
414  request.setRequestHeader("Content-Type", "application/json");
415  request.send(JSON.stringify(submitInfo));
416
417  //setInnerHTML('debug', request.responseText);
418 
419  var response = JSON.parse(request.responseText);
420  if (response.status != 'ok')
421  {
422    setFatalError(response.message);
423    return false;
424  }
425 
426  var msg = '<ul>';
427  for (var i = 0; i < response.messages.length; i++)
428  {
429    msg += '<li>' + response.messages[i];
430  }
431  msg += '</ul>';
432  setInnerHTML('done', msg);
433  Main.show('done');
434  Main.show('gorestart');
435
436}
437
438
439function setInnerHTML(id, html)
440{
441  var tag = document.getElementById(id);
442  if (!tag) alert('No tag with id='+id);
443  tag.innerHTML = html;
444}
445
446function setInputStatus(prefix, message, clazz)
447{
448  var tag = document.getElementById(prefix + '.status');
449  tag.className = 'status ' + clazz;
450 
451  setInnerHTML(prefix + '.message', message);
452  if (message)
453  {
454    Main.showInline(prefix + '.message');
455  }
456  else
457  {
458    Main.hide(prefix + '.message');
459  }
460}
461
462function setFatalError(message)
463{
464  setInnerHTML('errorMessage', message);
465  Main.show('errorMessage');
466  Main.hide('gonext');
467  Main.show('gorestart');
468}
469
470function goRestart()
471{
472  location.href = location.href;
473}
474</script>
475<style>
476
477.stepform
478{
479  margin-left: 20px;
480  border: 1px solid #999999;
481  width: 800px;
482  table-layout: fixed;
483}
484
485.stepno
486{
487  width: 20px;
488  font-size: 20px;
489  font-weight: bold;
490  color: #E0E0E0;
491  background: #555577;
492  vertical-align: top;
493  text-align: center;
494}
495
496.steptitle
497{
498  width: 780px;
499  color: #333377;
500  background: #E0E0E0;
501  font-weight: bold;
502  padding: 1px 4px 1px 4px;
503  border-bottom: 1px solid #999999;
504}
505
506.nextstep
507{
508  width: 780px;
509  color: #333377;
510  background: #E0E0E0;
511  font-weight: bold;
512  padding: 1px 4px 1px 4px;
513  border-top: 1px solid #999999;
514}
515
516.stepfields
517{
518  width: 780px;
519}
520
521.prompt
522{
523  width: 150px;
524  font-weight: bold;
525  padding: 1px 2px 1px 2px;
526}
527
528.input
529{
530  width: 250px;
531  padding: 1px 2px 1px 2px;
532}
533
534.status
535{
536  width: 30px;
537  padding: 1px 2px 1px 2px;
538}
539
540.help
541{
542  background: #e0e0e0;
543  width: 350px;
544  font-style: italic;
545  padding: 1px 2px 1px 2px;
546}
547
548.message
549{
550  color: #cc0000;
551  font-weight: bold;
552  padding-right: 6px;
553}
554
555.status.invalid:before
556{
557  content: url('../../images/error.gif');
558}
559.status.warning:before
560{
561  content: url('../../images/warning.gif');
562}
563.status.valid:before
564{
565  content: url('../../images/ok.gif');
566}
567.success ul
568{
569  list-style-image: url('../../images/ok.gif');
570}
571</style>
572</base:head>
573<base:body onload="init()">
574
575  <p:path style="margin-top: 20px; margin-bottom: 10px;">
576    <p:pathelement title="Reggie" href="<%="index.jsp?ID="+ID%>" />
577    <p:pathelement title="Personal information registration" />
578  </p:path>
579
580  <form name="reggie" onsubmit="return false;">
581 
582  <!-- 1. Case + Personal number -->
583  <table border="0" cellspacing="0" cellpadding="0" class="stepform">
584  <tr>
585    <td rowspan="3" class="stepno">1</td>
586    <td class="steptitle">Enter Case Name and Personal Number</td>
587  </tr>
588  <tr>
589    <td class="stepfields">
590      <table border="0" cellspacing="0" cellpadding="0" width="100%">
591      <tr valign="top">
592        <td class="prompt">Case name</td>
593        <td class="input"><input type="text" name="caseName" 
594          size="18" maxlength="12" onblur="caseNameOnChange()" onkeypress="focusOnEnter(event, 'personalNumber')"></td>
595        <td class="status" id="case.status"></td>
596        <td class="help"><span id="case.message" class="message" style="display: none;"></span>The case (barcode) associated with this patient.</td>
597      </tr>
598      <tr>
599        <td class="prompt">Personal number</td>
600        <td class="input"><input type="text" name="personalNumber" 
601          size="18" maxlength="12" onkeyup="personalNumberOnChange()" onkeypress="goNextOnTabOrEnter(event)"></td>
602        <td class="status" id="pnr.status"></td>
603        <td class="help"><span id="pnr.message" class="message" style="display: none;"></span>(YYYYMMDDZZZZ)</td>
604      </tr>
605      </table>
606    </td>
607  </tr>
608  </table>
609
610  <!-- 2. New patient registration -->
611  <div id="newPatientSection" style="display: none;">
612  <p>
613  <table border="0" cellspacing="0" cellpadding="0" class="stepform">
614  <tr>
615    <td rowspan="2" class="stepno">2</td>
616    <td class="steptitle">New patient: Enter all names</td>
617  </tr>
618  <tr>
619    <td class="stepfields">
620      <table border="0" cellspacing="0" cellpadding="0" width="100%">
621      <tr>
622        <td class="prompt">Patient code</td>
623        <td class="input"><input type="text" name="patientCode" 
624          value="" size="18" maxlength="12" 
625          onkeypress="focusOnEnter(event, 'familyName')"></td>
626        <td class="status" id="patientCode.status"></td>
627        <td class="help"><span id="patientCode.message" class="message" style="display: none;"></span></td>
628      </tr>
629      <tr valign="top">
630        <td class="prompt">Family name</td>
631        <td class="input"><input type="text" name="familyName" 
632          value="" size="35" maxlength="255"
633          onkeypress="focusOnEnter(event, 'allFirstNames')"></td>
634        <td class="status" id="familyName.status"></td>
635        <td class="help"><span id="familyName.message" class="message" style="display: none;"></span>Keep hyphens, keep åäö, replace all special accented letters [e.g. éèü etc] with standard alphabet character.</td>
636      </tr>
637      <tr valign="top">
638        <td class="prompt">All first names</td>
639        <td class="input"><input type="text" name="allFirstNames" 
640          size="35" maxlength="255" onkeypress="goNextOnTabOrEnter(event)"></td>
641        <td class="status" id="allFirstNames.status"></td>
642        <td class="help"><span id="allFirstNames.message" class="message" style="display: none;"></span>Type all names, see FamilyName comment on valid characters.</td>
643      </tr>
644      <tr>
645        <td class="prompt">Gender</td>
646        <td class="input" id="new.gender"></td>
647        <td class="status"></td>
648        <td class="help"></td>
649      </tr>
650      <tr>
651        <td class="prompt">Date of birth</td>
652        <td class="input" id="new.dateOfBirth"></td>
653        <td class="status"></td>
654        <td class="help"></td>
655      </tr>
656      </table>
657    </td>
658  </tr>
659  </table>
660  </div>
661 
662  <!-- 2b. Existing patient -->
663  <div id="existingPatientSection" style="display: none;">
664  <p>
665  <table border="0" cellspacing="0" cellpadding="0" class="stepform">
666  <tr>
667    <td rowspan="2" class="stepno">2</td>
668    <td class="steptitle">Existing patient: Verify names</td>
669  </tr>
670  <tr>
671    <td class="stepfields">
672      <table border="0" cellspacing="0" cellpadding="0" width="100%">
673      <tr>
674        <td class="prompt">Patient code</td>
675        <td class="input" id="existing.patientCode"></td>
676        <td class="status"></td>
677        <td class="help"></td>
678      </tr>
679      <tr>
680        <td class="prompt">Family name</td>
681        <td class="input" id="existing.familyName"></td>
682        <td class="status"></td>
683        <td class="help"></td>
684      </tr>
685      <tr>
686        <td class="prompt">All first names</td>
687        <td class="input" id="existing.allFirstNames"></td>
688        <td class="status"></td>
689        <td class="help"></td>
690      </tr>
691      <tr>
692        <td class="prompt">Gender</td>
693        <td class="input" id="existing.gender"></td>
694        <td class="status"></td>
695        <td class="help"></td>
696      </tr>
697      <tr>
698        <td class="prompt">Date of birth</td>
699        <td class="input" id="existing.dateOfBirth"></td>
700        <td class="status"></td>
701        <td class="help"></td>
702      </tr>
703      </table>
704    </td>
705  </tr>
706  </table>
707  </div>
708
709
710  <!-- 3. Case registration -->
711  <div id="caseSection" style="display: none;">
712  <p>
713  <table border="0" cellspacing="0" cellpadding="0" class="stepform">
714  <tr>
715    <td rowspan="2" class="stepno">3</td>
716    <td class="steptitle">About this case</td>
717  </tr>
718  <tr>
719    <td class="stepfields">
720      <table border="0" cellspacing="0" cellpadding="0" width="100%">
721      <tr valign="top">
722        <td class="prompt" id="laterality.prompt">Laterality</td>
723        <td class="input" id="laterality.input">
724          <input type="radio" name="laterality" value="LEFT" onclick="lateralityOnChange()">LEFT
725          <input type="radio" name="laterality" value="RIGHT" onclick="lateralityOnChange()">RIGHT
726        </td>
727        <td class="status" id="laterality.status"></td>
728        <td class="help"><span id="laterality.message" class="message" style="display: none;"></span></td>
729      </tr>
730      <tr valign="top">
731        <td class="prompt">Specimen tubes</td>
732        <td class="input" id="specimenTubes"><i>not found</i></td>
733        <td class="status" id="specimenTubes.status"></td>
734        <td class="help"><span id="specimenTubes.message" class="message" style="display: none;"></span>The specimen tube(s) associated with this case.</td>
735      </tr>
736      <tr id="reasonIfNoSpecimenSection" valign="top">
737        <td class="prompt">Reason if no specimen</td>
738        <td class="input"><textarea rows="3" cols="30" name="reasonIfNoSpecimen" value=""></textarea></td>
739        <td class="status"></td>
740        <td class="help">Comment why there was no specimen tubes in the delivery.</td>
741      </tr>
742      </table>
743    </td>
744  </tr>
745  </table>
746  </div>
747
748  <div class="error" id="errorMessage" style="display: none; width: 800px; margin-left: 20px; margin-bottom: 0px;"></div>
749
750  <div id="done" class="success" style="display: none; width: 800px; margin-left: 20px; margin-top: 20px;"></div>
751
752  <table style="margin-left: 20px; margin-top: 10px;">
753    <tr><td><base:button id="gonext" title="Next" image="gonext.gif" onclick="goNext()"/></td></tr>
754    <tr><td><base:button id="gocreate" title="Create" image="gonext.gif" onclick="goCreate()" style="display: none;"/></td></tr>
755    <tr><td><base:button id="gorestart" title="Restart" image="goback.gif" onclick="goRestart()" style="display: none;"/></td></tr>
756  </table>
757  </form>
758
759  <pre>
760  <div id="debug"></div>
761  </pre>
762 
763</base:body>
764</base:page>
765<%
766}
767finally
768{
769  if (dc != null) dc.close();
770}
771%>
Note: See TracBrowser for help on using the repository browser.