MediaWiki:Gadget-DiagnosticTree.js

From Painwiki
Revision as of 12:21, 9 May 2026 by Yatreyu (talk | contribs)
Jump to navigation Jump to search

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
/**
 * DiagnosticTree.js — v6 PATCH: choice node support
 *
 * THREE changes required. Search for each FIND block and replace
 * with the corresponding REPLACE block. Nothing else changes.
 *
 * ─────────────────────────────────────────────────────────────────
 * CHANGE 1 of 3 — Add 'choice' to NODE_META
 * ─────────────────────────────────────────────────────────────────
 *
 * FIND (the last entry in NODE_META, around line 13):
 *
 *     neuro_referral:   { color:'#b91c1c', icon:'', label:'Neurological Referral' }
 *
 * REPLACE WITH:
 *
 *     neuro_referral:   { color:'#b91c1c', icon:'', label:'Neurological Referral' },
 *     choice:           { color:'#7c3aed', icon:'', label:'Select Best Match' }
 *
 *
 * ─────────────────────────────────────────────────────────────────
 * CHANGE 2 of 3 — Route choice nodes to _choice() in render()
 * ─────────────────────────────────────────────────────────────────
 *
 * FIND (inside DiagnosticTree.prototype.render, around line 120):
 *
 *     if      ( node.type === 'result'        ) cardHtml = this._result( node );
 *     else if ( node.type === 'overlap'       ) cardHtml = this._overlap( node );
 *     else if ( node.type === 'neuro_referral') cardHtml = this._neuroRef( node );
 *     else                                      cardHtml = this._question( node, meta );
 *
 * REPLACE WITH:
 *
 *     if      ( node.type === 'result'        ) cardHtml = this._result( node );
 *     else if ( node.type === 'overlap'       ) cardHtml = this._overlap( node );
 *     else if ( node.type === 'neuro_referral') cardHtml = this._neuroRef( node );
 *     else if ( node.type === 'choice'        ) cardHtml = this._choice( node, meta );
 *     else                                      cardHtml = this._question( node, meta );
 *
 *
 * ─────────────────────────────────────────────────────────────────
 * CHANGE 3 of 3 — Wire option button clicks in render()
 *                 AND add the _choice() method
 * ─────────────────────────────────────────────────────────────────
 *
 * FIND (inside DiagnosticTree.prototype.render, the yes/no wiring
 *       block near the bottom, around line 140):
 *
 *     b( '#dt-back',  function () { s.state.back();  s.render(); } );
 *     b( '#dt-reset', function () { s.state.reset(); s.render(); } );
 *     b( '#dt-yes',   function () { var n = s.state.node(); if ( n.yes ) { s.state.go( n.yes ); s.render(); } } );
 *     b( '#dt-no',    function () { var n = s.state.node(); if ( n.no  ) { s.state.go( n.no  ); s.render(); } } );
 *
 * REPLACE WITH:
 *
 *     b( '#dt-back',  function () { s.state.back();  s.render(); } );
 *     b( '#dt-reset', function () { s.state.reset(); s.render(); } );
 *     b( '#dt-yes',   function () { var n = s.state.node(); if ( n.yes ) { s.state.go( n.yes ); s.render(); } } );
 *     b( '#dt-no',    function () { var n = s.state.node(); if ( n.no  ) { s.state.go( n.no  ); s.render(); } } );
 *
 *     this.el.querySelectorAll( '.dt-option-btn' ).forEach( function ( btn ) {
 *       btn.addEventListener( 'click', function () {
 *         var next = btn.getAttribute( 'data-next' );
 *         if ( next ) { s.state.go( next ); s.render(); }
 *       } );
 *     } );
 *
 *
 * ─────────────────────────────────────────────────────────────────
 * Then add _choice() as a new method.
 *
 * FIND (the opening line of the existing _question method):
 *
 *     DiagnosticTree.prototype._question = function ( node, meta ) {
 *
 * INSERT the entire _choice() method ABOVE that line:
 * ─────────────────────────────────────────────────────────────────
 */

// ─── INSERT THIS ENTIRE BLOCK above _question ───────────────────

DiagnosticTree.prototype._choice = function ( node, meta ) {
  var rat = node.clinical_rationale || '';

  var opts = node.options.map( function ( opt, i ) {
    return '<button class="dt-option-btn" data-opt-index="' + i + '" data-next="' + opt.next + '">' +
      '<div class="dt-option-content">' +
        '<span class="dt-option-label">' + opt.label + '</span>' +
        ( opt.sublabel
          ? '<span class="dt-option-sublabel">' + opt.sublabel + '</span>'
          : '' ) +
      '</div>' +
    '</button>';
  } ).join( '' );

  return '<div class="dt-card" style="border-color:' + meta.color + '22">' +
    '<div class="dt-type-badge" style="background:' + meta.color + '">' +
      meta.icon + ' ' + meta.label +
    '</div>' +
    '<div class="dt-question">' + node.question + '</div>' +
    ( rat ? '<div class="dt-rationale">' + rat + '</div>' : '' ) +
    '<div class="dt-options">' + opts + '</div>' +
  '</div>';
};

// ─── END INSERT ──────────────────────────────────────────────────


/**
 * ─────────────────────────────────────────────────────────────────
 * CSS ADDITIONS — append to MediaWiki:Gadget-DiagnosticTree.css
 * ─────────────────────────────────────────────────────────────────
 *
 * Add the following rules at the end of the existing CSS file.
 * They use only existing CSS variables — no new variables needed.
 */

/*

.dt-options {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin-top: 1rem;
}

.dt-option-btn {
  display: flex;
  align-items: flex-start;
  gap: 0.85rem;
  width: 100%;
  padding: 0.75rem 0.9rem;
  background: var(--paper-2);
  border: 1.5px solid var(--rule);
  border-radius: 8px;
  cursor: pointer;
  text-align: left;
  font-family: 'DM Sans', sans-serif;
  position: relative;
  overflow: hidden;
  transition: border-color 0.15s, background 0.15s, transform 0.1s, box-shadow 0.15s;
}

.dt-option-btn::before {
  content: '';
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 4px;
  background: var(--opt-color, var(--blue));
  border-radius: 8px 0 0 8px;
  transition: width 0.15s;
}

.dt-option-btn:hover {
  border-color: var(--opt-color, var(--blue));
  background: #fff;
  transform: translateY(-1px);
  box-shadow: 0 3px 10px rgba(0,0,0,0.08);
}

.dt-option-btn:hover::before { width: 6px; }
.dt-option-btn:active        { transform: translateY(0); box-shadow: none; }

.dt-option-btn[data-opt-index="0"] { --opt-color: var(--blue);   }
.dt-option-btn[data-opt-index="1"] { --opt-color: var(--teal);   }
.dt-option-btn[data-opt-index="2"] { --opt-color: var(--slate);  }
.dt-option-btn[data-opt-index="3"] { --opt-color: var(--urgent); }

.dt-option-btn[data-opt-index="0"]:hover { background: #eff6ff; border-color: var(--blue);   }
.dt-option-btn[data-opt-index="1"]:hover { background: #f0fdfa; border-color: var(--teal);   }
.dt-option-btn[data-opt-index="2"]:hover { background: #f8fafc; border-color: var(--slate);  }
.dt-option-btn[data-opt-index="3"]:hover { background: #fffbeb; border-color: var(--urgent); }

.dt-option-content {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  padding-left: 0.25rem;
}

.dt-option-label {
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--ink);
  line-height: 1.35;
}

.dt-option-sublabel {
  font-size: 0.75rem;
  color: var(--ink-soft);
  line-height: 1.4;
  font-weight: 400;
}

*/

/**
 * ─────────────────────────────────────────────────────────────────
 * SUMMARY OF ALL CHANGES
 * ─────────────────────────────────────────────────────────────────
 *
 * DiagnosticTree.js — 3 edits:
 *   1. Add 'choice' entry to NODE_META           (~1 line)
 *   2. Add choice branch in render() dispatcher  (~1 line)
 *   3. Add option button click wiring in render() (~5 lines)
 *   4. Add _choice() method above _question()    (~20 lines)
 *
 * DiagnosticTree.css — 1 edit:
 *   Append ~50 lines of new rules at the end of the file.
 *   All rules use only existing CSS variables.
 *   No existing rules are modified.
 *
 * Backward compatibility:
 *   All existing binary (yes/no) nodes are completely unchanged.
 *   The new code only activates when node.type === 'choice'.
 *   Existing EarTMJ and FrontOfChest trees work identically.
 *
 * JSON schema for a choice node:
 *   {
 *     "type": "choice",
 *     "question": "...",
 *     "clinical_rationale": "...",   <- optional
 *     "options": [
 *       { "label": "...", "sublabel": "...", "next": "node-id" },
 *       { "label": "...", "sublabel": "...", "next": "node-id" },
 *       { "label": "...", "sublabel": "...", "next": "node-id" }
 *     ]
 *   }
 *   sublabel is optional. Maximum 4 options (index 0–3 are styled).
 * ─────────────────────────────────────────────────────────────────
 */