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

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

References #291: Personal information registration

Added a 'Cancel' button that aborts the current registration.

File size: 19.8 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  Main.show('gocancel');
255 
256  // Get biosource information from the AJAX response
257  patientInfo = response.patientInfo;
258  caseInfo = response.caseInfo;
259 
260  if (!patientInfo.id)
261  {
262    Main.show('newPatientSection');
263    frm.patientCode.value=patientInfo.name;
264    setInnerHTML('new.dateOfBirth', patientInfo.dateOfBirth);
265    setInnerHTML('new.gender', patientInfo.gender);
266    frm.familyName.focus();
267  }
268  else
269  {
270    Main.show('existingPatientSection');
271    setInnerHTML('existing.patientCode', patientInfo.name);
272    setInnerHTML('existing.familyName', patientInfo.familyName);
273    setInnerHTML('existing.allFirstNames', patientInfo.allFirstNames);
274    setInnerHTML('existing.dateOfBirth', patientInfo.dateOfBirth);
275    setInnerHTML('existing.gender', patientInfo.gender);
276    gotoStep3();
277  }
278}
279
280function gotoStep3()
281{
282  // Check entered case and pnr with AJAX
283  var frm = document.forms['reggie'];
284 
285  if (!patientInfo.id)
286  {
287    frm.patientCode.disabled = true;
288    frm.familyName.disabled = true;
289    frm.allFirstNames.disabled = true;
290    patientInfo.familyName = frm.familyName.value;
291    patientInfo.allFirstNames = frm.allFirstNames.value;
292  }
293  currentStep = 3;
294 
295  // Generate list of specimen tubes
296  var thisCaseLaterality;
297  if (caseInfo.specimen && caseInfo.specimen.length > 0)
298  {
299    var specimenTubes = '';
300    Main.hide('reasonIfNoSpecimenSection');
301    for (var i = 0; i < caseInfo.specimen.length; i++)
302    {
303      var specimen = caseInfo.specimen[i];
304      specimenTubes += specimen.name ;
305      if (specimen.laterality) 
306      {
307        specimenTubes += ' ('+specimen.laterality + ')';
308        Forms.checkRadio(frm.laterality, specimen.laterality);
309       
310        if (thisCaseLaterality && thisCaseLaterality != specimen.laterality)
311        {
312          setInputStatus('specimenTubes', 'Specimen tubes with different laterality', 'warning');
313        }
314        thisCaseLaterality = specimen.laterality;
315      }
316      else
317      {
318        specimenTubes += ' (<i>unknown laterality</i>)';
319      }
320      specimenTubes += '<br>';
321    }
322    setInnerHTML('specimenTubes', specimenTubes);
323  }
324
325 
326  Main.show('caseSection');
327 
328  // Existing cases for this patient
329  var hasLeftCase = false;
330  var hasRightCase = false;
331  if (patientInfo.cases && patientInfo.cases.length > 0)
332  {
333    var cases = '';
334    for (var i = 0; i < patientInfo.cases.length; i++)
335    {
336      var cc = patientInfo.cases[i];
337      cases += '<input type="radio" name="laterality" value="' + cc.id + '"';
338      if (cc.laterality == 'LEFT') 
339      {
340        if (hasLeftCase) setInputStatus('laterality', 'Two cases with LEFT', 'warning');
341        hasLeftCase = true;
342      }
343      if (cc.laterality == 'RIGHT') 
344      {
345        if (hasRightCase) setInputStatus('laterality', 'Two cases with RIGHT', 'warning');
346        hasRightCase = true;
347      }
348      if (cc.laterality == thisCaseLaterality) cases += ' checked';
349      cases += ' onclick="lateralityOnChange()">' + cc.name;
350      if (cc.laterality)
351      {
352        cases += ' (' + cc.laterality + ')<br>';
353      }
354      else
355      {
356        cases += ' (<i>unknown laterality</i>)<br>';
357      }
358    }
359   
360    if (patientInfo.cases.length == 1)
361    {
362      if (!hasLeftCase)
363      {
364        cases += '<input type="radio" name="laterality" value="LEFT" ';
365        if (thisCaseLaterality == 'LEFT') cases += ' checked';
366        cases += ' onclick="lateralityOnChange()"><i>new case</i> (LEFT)<br>';
367      }
368      if (!hasRightCase)
369      {
370        cases += '<input type="radio" name="laterality" value="RIGHT"';
371        if (thisCaseLaterality == 'RIGHT') cases += ' checked';
372        cases += ' onclick="lateralityOnChange()"><i>new case</i> (RIGHT)<br>';
373      }
374    }
375   
376    if (patientInfo.cases.length == 2)
377    {
378      setInnerHTML('laterality.prompt', 'Merge with case');
379    }
380    else
381    {
382      setInnerHTML('laterality.prompt', 'Merge/create new case');
383    }
384   
385    setInnerHTML('laterality.input', cases);
386  }
387  lateralityOnChange();
388
389  Main.hide('gonext');
390  Main.show('gocreate');
391}
392
393function goCreate()
394{
395  if (!step3IsValid()) return;
396 
397  Main.hide('gocreate');
398  Main.hide('gocancel');
399  var frm = document.forms['reggie'];
400
401  caseInfo.laterality = Forms.getCheckedRadio(frm.laterality).value;
402  caseInfo.reasonIfNoSpecimen = frm.reasonIfNoSpecimen.value;
403
404  for (var i = 0; i < frm.laterality.length; i++)
405  {
406    frm.laterality[i].disabled = true;
407  }
408  frm.reasonIfNoSpecimen.disabled = true;
409
410  var submitInfo = new Object();
411  submitInfo.patientInfo = patientInfo;
412  submitInfo.caseInfo = caseInfo;
413
414  var request = Ajax.getXmlHttpRequest();
415  var url = 'PersonalRegistration.servlet?ID=<%=ID%>&cmd=Create';
416  request.open("POST", url, false);
417  request.setRequestHeader("Content-Type", "application/json");
418  request.send(JSON.stringify(submitInfo));
419
420  //setInnerHTML('debug', request.responseText);
421 
422  var response = JSON.parse(request.responseText);
423  if (response.status != 'ok')
424  {
425    setFatalError(response.message);
426    return false;
427  }
428 
429  var msg = '<ul>';
430  for (var i = 0; i < response.messages.length; i++)
431  {
432    msg += '<li>' + response.messages[i];
433  }
434  msg += '</ul>';
435  setInnerHTML('done', msg);
436  Main.show('done');
437  Main.show('gorestart');
438
439}
440
441
442function setInnerHTML(id, html)
443{
444  var tag = document.getElementById(id);
445  if (!tag) alert('No tag with id='+id);
446  tag.innerHTML = html;
447}
448
449function setInputStatus(prefix, message, clazz)
450{
451  var tag = document.getElementById(prefix + '.status');
452  tag.className = 'status ' + clazz;
453 
454  setInnerHTML(prefix + '.message', message);
455  if (message)
456  {
457    Main.showInline(prefix + '.message');
458  }
459  else
460  {
461    Main.hide(prefix + '.message');
462  }
463}
464
465function setFatalError(message)
466{
467  setInnerHTML('errorMessage', message);
468  Main.show('errorMessage');
469  Main.hide('gonext');
470  Main.hide('gocancel');
471  Main.show('gorestart');
472}
473
474function goRestart(force)
475{
476  if (!force && !confirm('Cancel this registration?')) return;
477  location.href = location.href;
478}
479</script>
480<style>
481
482.stepform
483{
484  margin-left: 20px;
485  border: 1px solid #999999;
486  width: 800px;
487  table-layout: fixed;
488}
489
490.stepno
491{
492  width: 20px;
493  font-size: 20px;
494  font-weight: bold;
495  color: #E0E0E0;
496  background: #555577;
497  vertical-align: top;
498  text-align: center;
499}
500
501.steptitle
502{
503  width: 780px;
504  color: #333377;
505  background: #E0E0E0;
506  font-weight: bold;
507  padding: 1px 4px 1px 4px;
508  border-bottom: 1px solid #999999;
509}
510
511.nextstep
512{
513  width: 780px;
514  color: #333377;
515  background: #E0E0E0;
516  font-weight: bold;
517  padding: 1px 4px 1px 4px;
518  border-top: 1px solid #999999;
519}
520
521.stepfields
522{
523  width: 780px;
524}
525
526.prompt
527{
528  width: 150px;
529  font-weight: bold;
530  padding: 1px 2px 1px 2px;
531}
532
533.input
534{
535  width: 250px;
536  padding: 1px 2px 1px 2px;
537}
538
539.status
540{
541  width: 30px;
542  padding: 1px 2px 1px 2px;
543}
544
545.help
546{
547  background: #e0e0e0;
548  width: 350px;
549  font-style: italic;
550  padding: 1px 2px 1px 2px;
551}
552
553.message
554{
555  color: #cc0000;
556  font-weight: bold;
557  padding-right: 6px;
558}
559
560.status.invalid:before
561{
562  content: url('../../images/error.gif');
563}
564.status.warning:before
565{
566  content: url('../../images/warning.gif');
567}
568.status.valid:before
569{
570  content: url('../../images/ok.gif');
571}
572.success ul
573{
574  list-style-image: url('../../images/ok.gif');
575}
576</style>
577</base:head>
578<base:body onload="init()">
579
580  <p:path style="margin-top: 20px; margin-bottom: 10px;">
581    <p:pathelement title="Reggie" href="<%="index.jsp?ID="+ID%>" />
582    <p:pathelement title="Personal information registration" />
583  </p:path>
584
585  <form name="reggie" onsubmit="return false;">
586 
587  <!-- 1. Case + Personal number -->
588  <table border="0" cellspacing="0" cellpadding="0" class="stepform">
589  <tr>
590    <td rowspan="3" class="stepno">1</td>
591    <td class="steptitle">Enter Case Name and Personal Number</td>
592  </tr>
593  <tr>
594    <td class="stepfields">
595      <table border="0" cellspacing="0" cellpadding="0" width="100%">
596      <tr valign="top">
597        <td class="prompt">Case name</td>
598        <td class="input"><input type="text" name="caseName" 
599          size="18" maxlength="12" onblur="caseNameOnChange()" onkeypress="focusOnEnter(event, 'personalNumber')"></td>
600        <td class="status" id="case.status"></td>
601        <td class="help"><span id="case.message" class="message" style="display: none;"></span>The case (barcode) associated with this patient.</td>
602      </tr>
603      <tr>
604        <td class="prompt">Personal number</td>
605        <td class="input"><input type="text" name="personalNumber" 
606          size="18" maxlength="12" onkeyup="personalNumberOnChange()" onkeypress="goNextOnTabOrEnter(event)"></td>
607        <td class="status" id="pnr.status"></td>
608        <td class="help"><span id="pnr.message" class="message" style="display: none;"></span>(YYYYMMDDZZZZ)</td>
609      </tr>
610      </table>
611    </td>
612  </tr>
613  </table>
614
615  <!-- 2. New patient registration -->
616  <div id="newPatientSection" style="display: none;">
617  <p>
618  <table border="0" cellspacing="0" cellpadding="0" class="stepform">
619  <tr>
620    <td rowspan="2" class="stepno">2</td>
621    <td class="steptitle">New patient: Enter all names</td>
622  </tr>
623  <tr>
624    <td class="stepfields">
625      <table border="0" cellspacing="0" cellpadding="0" width="100%">
626      <tr>
627        <td class="prompt">Patient code</td>
628        <td class="input"><input type="text" name="patientCode" 
629          value="" size="18" maxlength="12" 
630          onkeypress="focusOnEnter(event, 'familyName')"></td>
631        <td class="status" id="patientCode.status"></td>
632        <td class="help"><span id="patientCode.message" class="message" style="display: none;"></span></td>
633      </tr>
634      <tr valign="top">
635        <td class="prompt">Family name</td>
636        <td class="input"><input type="text" name="familyName" 
637          value="" size="35" maxlength="255"
638          onkeypress="focusOnEnter(event, 'allFirstNames')"></td>
639        <td class="status" id="familyName.status"></td>
640        <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>
641      </tr>
642      <tr valign="top">
643        <td class="prompt">All first names</td>
644        <td class="input"><input type="text" name="allFirstNames" 
645          size="35" maxlength="255" onkeypress="goNextOnTabOrEnter(event)"></td>
646        <td class="status" id="allFirstNames.status"></td>
647        <td class="help"><span id="allFirstNames.message" class="message" style="display: none;"></span>Type all names, see FamilyName comment on valid characters.</td>
648      </tr>
649      <tr>
650        <td class="prompt">Gender</td>
651        <td class="input" id="new.gender"></td>
652        <td class="status"></td>
653        <td class="help"></td>
654      </tr>
655      <tr>
656        <td class="prompt">Date of birth</td>
657        <td class="input" id="new.dateOfBirth"></td>
658        <td class="status"></td>
659        <td class="help"></td>
660      </tr>
661      </table>
662    </td>
663  </tr>
664  </table>
665  </div>
666 
667  <!-- 2b. Existing patient -->
668  <div id="existingPatientSection" style="display: none;">
669  <p>
670  <table border="0" cellspacing="0" cellpadding="0" class="stepform">
671  <tr>
672    <td rowspan="2" class="stepno">2</td>
673    <td class="steptitle">Existing patient: Verify names</td>
674  </tr>
675  <tr>
676    <td class="stepfields">
677      <table border="0" cellspacing="0" cellpadding="0" width="100%">
678      <tr>
679        <td class="prompt">Patient code</td>
680        <td class="input" id="existing.patientCode"></td>
681        <td class="status"></td>
682        <td class="help"></td>
683      </tr>
684      <tr>
685        <td class="prompt">Family name</td>
686        <td class="input" id="existing.familyName"></td>
687        <td class="status"></td>
688        <td class="help"></td>
689      </tr>
690      <tr>
691        <td class="prompt">All first names</td>
692        <td class="input" id="existing.allFirstNames"></td>
693        <td class="status"></td>
694        <td class="help"></td>
695      </tr>
696      <tr>
697        <td class="prompt">Gender</td>
698        <td class="input" id="existing.gender"></td>
699        <td class="status"></td>
700        <td class="help"></td>
701      </tr>
702      <tr>
703        <td class="prompt">Date of birth</td>
704        <td class="input" id="existing.dateOfBirth"></td>
705        <td class="status"></td>
706        <td class="help"></td>
707      </tr>
708      </table>
709    </td>
710  </tr>
711  </table>
712  </div>
713
714
715  <!-- 3. Case registration -->
716  <div id="caseSection" style="display: none;">
717  <p>
718  <table border="0" cellspacing="0" cellpadding="0" class="stepform">
719  <tr>
720    <td rowspan="2" class="stepno">3</td>
721    <td class="steptitle">About this case</td>
722  </tr>
723  <tr>
724    <td class="stepfields">
725      <table border="0" cellspacing="0" cellpadding="0" width="100%">
726      <tr valign="top">
727        <td class="prompt" id="laterality.prompt">Laterality</td>
728        <td class="input" id="laterality.input">
729          <input type="radio" name="laterality" value="LEFT" onclick="lateralityOnChange()">LEFT
730          <input type="radio" name="laterality" value="RIGHT" onclick="lateralityOnChange()">RIGHT
731        </td>
732        <td class="status" id="laterality.status"></td>
733        <td class="help"><span id="laterality.message" class="message" style="display: none;"></span></td>
734      </tr>
735      <tr valign="top">
736        <td class="prompt">Specimen tubes</td>
737        <td class="input" id="specimenTubes"><i>not found</i></td>
738        <td class="status" id="specimenTubes.status"></td>
739        <td class="help"><span id="specimenTubes.message" class="message" style="display: none;"></span>The specimen tube(s) associated with this case.</td>
740      </tr>
741      <tr id="reasonIfNoSpecimenSection" valign="top">
742        <td class="prompt">Reason if no specimen</td>
743        <td class="input"><textarea rows="3" cols="30" name="reasonIfNoSpecimen" value=""></textarea></td>
744        <td class="status"></td>
745        <td class="help">Comment why there was no specimen tubes in the delivery.</td>
746      </tr>
747      </table>
748    </td>
749  </tr>
750  </table>
751  </div>
752
753  <div class="error" id="errorMessage" style="display: none; width: 800px; margin-left: 20px; margin-bottom: 0px;"></div>
754
755  <div id="done" class="success" style="display: none; width: 800px; margin-left: 20px; margin-top: 20px;"></div>
756
757  <table style="margin-left: 20px; margin-top: 10px;">
758  <tr>
759    <td><base:button id="gocancel" title="Cancel" onclick="goRestart(false)" style="display: none;"/></td>
760    <td><base:button id="gonext" title="Next" image="gonext.gif" onclick="goNext()"/></td>
761    <td><base:button id="gocreate" title="Create" image="gonext.gif" onclick="goCreate()" style="display: none;"/></td>
762    <td><base:button id="gorestart" title="Restart" image="goback.gif" onclick="goRestart(true)" style="display: none;"/></td>
763  </tr>
764  </table>
765  </form>
766
767  <pre>
768  <div id="debug"></div>
769  </pre>
770 
771</base:body>
772</base:page>
773<%
774}
775finally
776{
777  if (dc != null) dc.close();
778}
779%>
Note: See TracBrowser for help on using the repository browser.