View Categories

09 HTML-Beispiele

Überblick

Die folgenden Beispiele zeigen typische Einsatzmöglichkeiten der MCDMS WebAkte anhand vollständiger Formularthemen.

Anders als im Kapitel 08 HTML-Formulare werden hier nicht einzelne HTML-Attribute oder JavaScript-Funktionen erklärt. Jedes Beispiel bildet einen vollständigen Anwendungsfall ab.

Zu jedem Beispiel gehören – soweit erforderlich:

  • Datenbankstruktur
  • FORMULARE.INI
  • HTML-Formular
  • MCDMS-Scripts
  • Rechte
  • besondere Laufzeitlogik

Die vollständigen HTML-Dateien können jeweils aufgeklappt und als Ausgangspunkt für eigene Formulare kopiert werden.

1. Verlaufsdokumentation

Ziel

Dieses Beispiel zeigt ein klassisches SQL-MultiRecord-Formular.

Ein Patient kann beliebig viele Verlaufseinträge besitzen. Jeder Eintrag enthält beispielsweise:

  • Datum
  • Ersteller
  • Zeitpunkt
  • Freitext

Das Beispiel eignet sich als Ausgangspunkt für:

  • Verlaufsdokumentation
  • Gesprächsnotizen
  • Pflegeeinträge
  • Therapieverläufe
  • ärztliche Kurznotizen

Datenmodell

Beispiel:

FRM_VERLAUF

FRMID          int Identity, Primärschlüssel
PatientID      int
DATUM          date
ERSTELLTAM     datetime2
ERSTELLTVON    nvarchar(100)
TEXT           nvarchar(max)

FRMID und PatientID werden nicht vom Browser bestimmt.

FORMULARE.INI

[FORMULAR_VERLAUF]
Title=Verlauf
TitleRow=[#DATUM#] | [#ERSTELLTVON#]

HTML=HTML\Arzt\VERLAUF.html

Edit=TRUE
MultiRecord=TRUE
Delete=TRUE
AllowDuplicate=TRUE
DoubleSaveButton=TRUE

Database=FRM_VERLAUF;FRMID;PatientID
OrderBy=FRMID;DESC

MATCHCODE=VERLAUF

Durch:

MultiRecord=TRUE

können mehrere Einträge pro Patient gespeichert werden.

Mit:

TitleRow=[#DATUM#] | [#ERSTELLTVON#]

erhält jeder Datensatz in der WebAkte eine aussagekräftige Bezeichnung.

MAIN.HTML
Copy
<!doctype html>
<html lang="de">

<head>
    <meta charset="utf-8">
    <meta name="viewport"
          content="width=device-width, initial-scale=1.0">

    <title>Verlauf</title>

    <style>
        body {
            margin: 0;
            background: transparent;
        }

        .verlauf-form,
        .verlauf-form * {
            box-sizing: border-box;
        }

        .verlauf-form {
            max-width: 1000px;
            margin: 0 auto;
            padding: 20px;
            font-family: "Segoe UI", Arial, sans-serif;
        }

        .verlauf-card {
            background: #ffffff;
            border: 1px solid #d8e0e8;
            border-radius: 10px;
            padding: 20px;
        }

        .verlauf-grid {
            display: grid;
            grid-template-columns: 220px 1fr;
            gap: 16px;
            margin-bottom: 16px;
        }

        .verlauf-form label {
            display: grid;
            gap: 6px;
        }

        .verlauf-form input,
        .verlauf-form textarea {
            width: 100%;
            padding: 8px 10px;
            border: 1px solid #cbd5e1;
            border-radius: 6px;
            background: #ffffff;
            color: #1a202c;
        }

        .verlauf-form textarea {
            min-height: 180px;
            resize: vertical;
        }

        @media (max-width: 700px) {
            .verlauf-grid {
                grid-template-columns: 1fr;
            }
        }
    </style>
</head>

<body>

<form class="verlauf-form">

    <input type="hidden"
           name="ERSTELLTVON"
           data-field="ERSTELLTVON"
           value="[#ERSTELLTVON#]">

    <input type="hidden"
           name="ERSTELLTAM"
           data-field="ERSTELLTAM"
           value="[#ERSTELLTAM#]">

    <div class="verlauf-card">

        <h2>Verlaufsdokumentation</h2>

        <div class="verlauf-grid">

            <label>
                Datum
                <input type="date"
                       name="DATUM"
                       data-field="DATUM">
            </label>

            <div>
                Patient:<br>
                <strong>[#GESAMTNAME#]</strong>
            </div>

        </div>

        <label>
            Verlauf
            <textarea name="TEXT"
                      data-field="TEXT"></textarea>
        </label>

    </div>

</form>

</body>
</html>

Das Formular enthält beispielsweise:

DATUM
ERSTELLTAM
ERSTELLTVON
TEXT

Auditfelder können als Hidden-Felder Bestandteil des Masterformulars sein.

Ablauf

Beim Öffnen eines vorhandenen Eintrags werden die Werte aus der Datenbank geladen.

Bei + Neu wird zunächst ein neuer Formularzustand dargestellt. Erst beim Speichern wird der neue Datensatz angelegt und erhält seine technische FRMID.

Durch AllowDuplicate=TRUE kann ein vorhandener Verlaufseintrag zusätzlich als Ausgangspunkt für einen neuen Datensatz verwendet werden.

2. Diagnosen

Ziel

Dieses Beispiel zeigt ein umfangreiches RecordView=LIST-Formular.

Alle Diagnosen eines Patienten werden gemeinsam innerhalb einer Seite bearbeitet.

Die Diagnosen sind fachlich in mehrere Bereiche gegliedert:

Einweisung
Aufnahme
Behandlung
Entlassung

Jeder Bereich kann unabhängig auf- und zugeklappt werden und besitzt einen eigenen Button:

+ Diagnose hinzufügen

Das aktuelle Diagnosenformular verwendet tatsächlich mehrere benannte Recordlisten für diese Bereiche und ordnet sie über DIAGTYP zu. Diagnosen(3)

Datenmodell

Alle Bereiche verwenden dieselbe Diagnosentabelle.

Wichtige Felder sind beispielsweise:

ID
PatientID
DIAGTYP
ICDRANG
DIAGNOSE
ICD
SICH
LOKAL
ERG
DIAGDATUM
FREIG
ALPHASEID

Dabei haben zwei Felder eine besondere Bedeutung.

DIAGTYP

DIAGTYP bestimmt den fachlichen Bereich.

Beispielsweise:

1 = Einweisung
2 = Aufnahme
3 = Behandlung
4 = Entlassung

Der Anwender muss diesen technischen Wert nicht selbst eingeben.

Wird eine neue Diagnose beispielsweise unter Entlassung hinzugefügt, erhält die neue Zeile automatisch den für diesen Bereich vorgesehenen DIAGTYP.

ICDRANG

ICDRANG enthält die dauerhafte Reihenfolge der Diagnosen.

Beispiel:

1
2
3
4
5

Verschiebt der Anwender eine Diagnose mit den Pfeilschaltflächen, ändert sich zunächst die sichtbare Reihenfolge.

Beim Speichern wird ICDRANG entsprechend der neuen Reihenfolge wieder normalisiert.

Damit bleibt die Sortierung auch nach erneutem Öffnen erhalten.

FORMULARE.INI

Beispiel:

[FORMULAR_DIAGNOSEN]
Title=Diagnosen

HTML=HTML\Arzt\Diagnosen.html

Edit=TRUE
MultiRecord=TRUE
Delete=TRUE
AllowDuplicate=TRUE

RecordView=LIST

Database=DIAGNOSEN;ID;PatientID
OrderBy=ICDRANG;ASC

ScriptSaveHTML=CONFIG\Scripts\Diagnosen.MCFSCR

Recordlisten

Im HTML existieren mehrere sichtbare Bereiche.

Beispielsweise:

<tbody
    data-mcdms-record-list="EINWEISUNG"
    data-filter-field="DIAGTYP"
    data-filter-value="1">
</tbody>

Aufnahme:

<tbody
    data-mcdms-record-list="AUFNAHME"
    data-filter-field="DIAGTYP"
    data-filter-value="2">
</tbody>

Behandlung:

<tbody
    data-mcdms-record-list="BEHANDLUNG"
    data-filter-field="DIAGTYP"
    data-filter-value="3">
</tbody>

Entlassung:

<tbody
    data-mcdms-record-list="ENTLASSUNG"
    data-filter-field="DIAGTYP"
    data-filter-value="4">
</tbody>

Reihenfolge

Im gemeinsamen Recordtemplate befindet sich beispielsweise:

<input type="hidden"
       data-record-field="ICDRANG"
       data-mcdms-record-order>

data-mcdms-record-order kennzeichnet das Feld als persistente Reihenfolge.

Die LIST-Laufzeit sammelt die einzelnen Recordlisten und deren Felder getrennt vom normalen Master-/Detailmodell. app

Bedienung

Jede Diagnose kann:

  • geändert
  • nach oben verschoben
  • nach unten verschoben
  • dupliziert
  • gelöscht

werden.

Neue Diagnosen werden direkt im gewünschten Bereich angelegt.

Die sichtbare Nummer:

Nr.

ist nur die laufende Nummer innerhalb des Bereichs.

Die dauerhaft gespeicherte Reihenfolge befindet sich in:

ICDRANG

ICD-Suche

Zusätzlich kann das Formular eine eigene ICD-Suche anbieten.

Nach Auswahl eines Eintrags können beispielsweise:

ICD
DIAGNOSE

automatisch übernommen werden.

Diese Suchfunktion gehört zum Formular selbst und ist keine spezielle Backendfunktion des WebServers.

ScriptSaveHTML

Nach erfolgreichem Speichern des gesamten LIST-Bestands kann:

ScriptSaveHTML=CONFIG\Scripts\Diagnosen.MCFSCR

ausgeführt werden.

Damit lässt sich die vorhandene MCDMS-Fachlogik nach dem Speichern weiterverwenden.

MAIN.HTML
Copy
<!DOCTYPE html>
<html lang="de">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Diagnosen</title>
  <style>
    body{margin:0;padding:0;background:transparent}
    .mcdms-diagnosen,.mcdms-diagnosen *{box-sizing:border-box}
    .mcdms-diagnosen{width:100%;padding:14px;font-family:"Segoe UI",Arial,sans-serif;color:#1f2937;background:transparent}
    .diag-group{margin:0 0 14px}
    .diag-group-header{display:flex;align-items:center;gap:8px;padding:11px 14px;border:1px solid #d4dbe4;border-radius:9px;cursor:pointer;user-select:none;font-size:16px;font-weight:600;color:#334155}
    .diag-group-header::before{content:"▶";width:16px;font-size:12px;color:#475569;transition:transform .15s ease}
    .diag-group.open .diag-group-header::before{transform:rotate(90deg)}
    .diag-group.einweisung .diag-group-header{background:#f0e6ed}
    .diag-group.aufnahme .diag-group-header{background:#f8f1c8}
    .diag-group.behandlung .diag-group-header{background:#e3f9d4}
    .diag-group.entlassung .diag-group-header{background:#d1effc}
    .diag-group-body{display:none;margin:2px 0 0 0px}
    .diag-group.open .diag-group-body{display:block}
    .diag-table-wrap{width:100%;overflow-x:auto;border:1px solid #a8b4c1;background:#fff}
    .diag-table{width:100%;min-width:1080px;border-collapse:collapse;table-layout:fixed;background:#fff}
    .diag-table th{padding:6px;border-right:1px solid #cbd5e1;border-bottom:1px solid #aeb8c4;background:#e8edf3;color:#223047;font-size:11px;font-weight:600;text-align:center;white-space:nowrap}
    .diag-table th.col-text{text-align:left}
    .diag-table td{padding:3px;border-right:1px solid #d9e0e7;border-bottom:1px solid #dfe5eb;background:#fff;vertical-align:middle}
    .diag-table tr:last-child td{border-bottom:0}
    .diag-table th:last-child,.diag-table td:last-child{border-right:0}
    .diag-input{width:100%;height:29px;padding:4px 6px;border:1px solid #c7d2df;border-radius:4px;background:#fff;color:#111827;font:inherit;font-size:14px}
    .diag-input:focus{outline:none;border-color:#2b6cb0;box-shadow:0 0 0 2px rgba(43,108,176,.13)}
    .diag-input[type="date"]{width:108px;min-width:108px;max-width:108px}
    .diag-input[type="date"].date-empty::-webkit-datetime-edit{color:transparent}
    .diag-input[type="date"].date-empty::-webkit-datetime-edit-fields-wrapper{color:transparent}

    .diag-search-wrapper{display:flex;align-items:center;gap:6px;width:100%}
    .diag-search-wrapper .diag-input{flex:1;min-width:0}
    .diag-select-trigger{width:34px;height:29px;flex:0 0 34px;padding:0;border:1px solid #c7d2df;border-radius:4px;background:#f1f5f9;color:#475569;cursor:pointer;font-size:14px}
    .diag-select-trigger:hover{background:#e2e8f0;color:#1e293b}

    .diag-modal-backdrop{display:none;position:fixed;inset:0;background:rgba(0,0,0,.40);z-index:10000;align-items:center;justify-content:center;padding:18px}
    .diag-modal-content{width:min(680px,95vw);max-height:82vh;background:#fff;color:#1e293b;border-radius:12px;box-shadow:0 8px 30px rgba(0,0,0,.22);padding:20px;display:flex;flex-direction:column}
    .diag-modal-header{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:14px;font-size:16px;font-weight:600}
    .diag-modal-close{border:0;background:transparent;color:#94a3b8;cursor:pointer;font-size:20px;line-height:1;padding:2px 4px}
    .diag-modal-search{width:100%;height:40px;padding:8px 11px;border:1px solid #b9c5d1;border-radius:5px;background:#fff;color:#334155;font-size:14px;outline:none}
    .diag-modal-search:focus{border-color:#2b6cb0;box-shadow:0 0 0 2px rgba(43,108,176,.13)}
    .diag-modal-list{list-style:none;padding:0;margin:14px 0 0;overflow-y:auto;min-height:100px;max-height:52vh}
    .diag-modal-item{display:grid;grid-template-columns:90px 1fr;gap:12px;padding:9px 10px;border:1px solid #e2e8f0;border-radius:5px;margin-bottom:6px;background:#fff;cursor:pointer}
    .diag-modal-item:hover{background:#f1f5f9;border-color:#cbd5e1}
    .diag-modal-code{font-weight:700;color:#334155}
    .diag-modal-text{color:#475569}
    .diag-modal-empty{padding:12px;color:#94a3b8;font-style:italic}
    .diag-modal-footer{margin-top:14px}
    .diag-modal-cancel{width:100%;height:38px;border:1px solid #cbd5e1;border-radius:5px;background:#f8fafc;color:#334155;cursor:pointer}
    .diag-modal-cancel:hover{background:#eef2f7}
    .diag-center{text-align:center}
    .diag-check-cell{text-align:center}
    .diag-check{
      appearance:none;
      -webkit-appearance:none;
      width:17px;
      height:17px;
      margin:0;
      border:1px solid #9aa7b5;
      border-radius:3px;
      background:#fff;
      cursor:pointer;
      vertical-align:middle;
      display:inline-grid;
      place-content:center;
    }
    .diag-check:checked{
      background:#2b6cb0;
      border-color:#2b6cb0;
    }
    .diag-check:checked::after{
      content:"✓";
      color:#fff;
      font-size:13px;
      font-weight:700;
      line-height:15px;
    }
    .record-order{display:inline-flex;align-items:center;justify-content:center;min-width:28px;height:28px;font-size:12px;font-weight:600;color:#334155}
    .col-nr{width:44px;text-align:center}.col-text{width:auto}.col-icd{width:88px}.col-sich{width:60px}.col-lokal{width:60px}.col-erg{width:58px}.col-date{width:118px}.col-check{width:56px}.col-alpha{width:105px}.col-actions{width:132px}
    .record-actions{
      display:grid;
      grid-template-columns:28px 28px 28px 28px;
      align-items:center;
      justify-content:center;
      gap:4px;
      white-space:nowrap;
    }
    .record-actions [data-mcdms-record-move-up]{grid-column:1}
    .record-actions [data-mcdms-record-move-down]{grid-column:2}
    .record-actions [data-mcdms-record-duplicate]{grid-column:3}
    .record-actions [data-mcdms-record-delete]{grid-column:4}
    .row-action{width:28px;height:28px;padding:0;border:1px solid #b8c4d1;border-radius:4px;background:#f8fafc;color:#475569;cursor:pointer;font-size:13px;line-height:1}
    .row-action:hover:not(:disabled){background:#edf4fb;border-color:#8eabc8;color:#1f4f7a}
    .row-action:disabled{opacity:.35;cursor:default}
    .row-action.delete{color:#c93434}
    .row-action.delete:hover:not(:disabled){background:#fff1f1;border-color:#e3a7a7;color:#b42323}
    .add-row-wrap{display:flex;justify-content:flex-end;margin-top:7px}
    .btn-add-row{border:1px solid #9eb9dc;border-radius:5px;background:#fff;color:#2b6cb0;padding:6px 12px;font-size:14px;cursor:pointer}
    .btn-add-row:hover{background:#eef5fc}
    @media(max-width:760px){.mcdms-diagnosen{padding:8px}.diag-group-body{margin-left:0}.diag-group-header{font-size:15px}}
  </style>
</head>
<body>
<div class="mcdms-diagnosen">
<div id="icd-data" hidden>[#ICD#]</div>

  <section class="diag-group einweisung">
    <div class="diag-group-header" data-title="Einweisung">Einweisung</div>
    <div class="diag-group-body">
      <div class="diag-table-wrap"><table class="diag-table">
        <thead><tr><th class="col-nr">Nr.</th><th class="col-text">Diagnose</th><th class="col-icd">ICD</th><th class="col-sich">Sich.</th><th class="col-lokal">Lokal.</th><th class="col-erg">Erg.</th><th class="col-date">Diag. Datum</th><th class="col-check">Freig.</th><th class="col-alpha">Alpha-SE ID</th><th class="col-actions">Aktionen</th></tr></thead>
        <tbody class="diag-list" data-mcdms-record-list="EINWEISUNG" data-filter-field="DIAGTYP" data-filter-value="1"></tbody>
      </table></div>
      <div class="add-row-wrap"><button type="button" class="btn-add-row" data-mcdms-record-add="EINWEISUNG">+ Diagnose hinzufügen</button></div>
    </div>
  </section>

  <section class="diag-group aufnahme">
    <div class="diag-group-header" data-title="Aufnahme">Aufnahme</div>
    <div class="diag-group-body">
      <div class="diag-table-wrap"><table class="diag-table">
        <thead><tr><th class="col-nr">Nr.</th><th class="col-text">Diagnose</th><th class="col-icd">ICD</th><th class="col-sich">Sich.</th><th class="col-lokal">Lokal.</th><th class="col-erg">Erg.</th><th class="col-date">Diag. Datum</th><th class="col-check">Freig.</th><th class="col-alpha">Alpha-SE ID</th><th class="col-actions">Aktionen</th></tr></thead>
        <tbody class="diag-list" data-mcdms-record-list="AUFNAHME" data-filter-field="DIAGTYP" data-filter-value="2"></tbody>
      </table></div>
      <div class="add-row-wrap"><button type="button" class="btn-add-row" data-mcdms-record-add="AUFNAHME">+ Diagnose hinzufügen</button></div>
    </div>
  </section>

  <section class="diag-group behandlung">
    <div class="diag-group-header" data-title="Behandlung">Behandlung</div>
    <div class="diag-group-body">
      <div class="diag-table-wrap"><table class="diag-table">
        <thead><tr><th class="col-nr">Nr.</th><th class="col-text">Diagnose</th><th class="col-icd">ICD</th><th class="col-sich">Sich.</th><th class="col-lokal">Lokal.</th><th class="col-erg">Erg.</th><th class="col-date">Diag. Datum</th><th class="col-check">Freig.</th><th class="col-alpha">Alpha-SE ID</th><th class="col-actions">Aktionen</th></tr></thead>
        <tbody class="diag-list" data-mcdms-record-list="BEHANDLUNG" data-filter-field="DIAGTYP" data-filter-value="3"></tbody>
      </table></div>
      <div class="add-row-wrap"><button type="button" class="btn-add-row" data-mcdms-record-add="BEHANDLUNG">+ Diagnose hinzufügen</button></div>
    </div>
  </section>

  <section class="diag-group entlassung open">
    <div class="diag-group-header" data-title="Entlassung">Entlassung</div>
    <div class="diag-group-body">
      <div class="diag-table-wrap"><table class="diag-table">
        <thead><tr><th class="col-nr">Nr.</th><th class="col-text">Diagnose</th><th class="col-icd">ICD</th><th class="col-sich">Sich.</th><th class="col-lokal">Lokal.</th><th class="col-erg">Erg.</th><th class="col-date">Diag. Datum</th><th class="col-check">Freig.</th><th class="col-alpha">Alpha-SE ID</th><th class="col-actions">Aktionen</th></tr></thead>
        <tbody class="diag-list" data-mcdms-record-list="ENTLASSUNG" data-filter-field="DIAGTYP" data-filter-value="0"></tbody>
      </table></div>
      <div class="add-row-wrap"><button type="button" class="btn-add-row" data-mcdms-record-add="ENTLASSUNG">+ Diagnose hinzufügen</button></div>
    </div>
  </section>

  <template data-mcdms-record-row>
    <tr>
      <td class="col-nr"><span class="record-order" data-mcdms-record-order></span></td>
      <td><div class="diag-search-wrapper">
        <input class="diag-input" type="text" data-record-field="Diagnose">
        <button type="button" class="diag-select-trigger" data-icd-select title="Diagnose auswählen">🔍</button>
      </div></td>
      <td><input class="diag-input diag-center" type="text" data-record-field="ICDNR"></td>
      <td><input class="diag-input diag-center" type="text" data-record-field="Sicherheit"></td>
      <td><input class="diag-input diag-center" type="text" data-record-field="Lokalisation"></td>
      <td><input class="diag-input diag-center" type="text" data-record-field="Behandlungsergebnis"></td>
      <td><input class="diag-input diag-center" type="date" data-record-field="DiagDatum"></td>
      <td class="diag-check-cell"><input class="diag-check" type="checkbox" data-record-field="Freigabe"></td>
      <td><input class="diag-input diag-center" type="text" data-record-field="ORPHAID"></td>
      <td class="col-actions"><div class="record-actions">
        <button type="button" class="row-action" data-mcdms-record-move-up title="Nach oben">▲</button>
        <button type="button" class="row-action" data-mcdms-record-move-down title="Nach unten">▼</button>
        <button type="button" class="row-action" data-mcdms-record-duplicate title="Duplizieren">📋</button>
        <button type="button" class="row-action delete" data-mcdms-record-delete title="Löschen">✕</button>
      </div></td>
    </tr>
  </template>

  <div class="diag-modal-backdrop" id="icdModal">
    <div class="diag-modal-content" role="dialog" aria-modal="true" aria-labelledby="icdModalTitle">
      <div class="diag-modal-header">
        <span id="icdModalTitle">Diagnose auswählen</span>
        <button type="button" class="diag-modal-close" id="icdModalClose" title="Schließen">✕</button>
      </div>

      <input type="text"
             class="diag-modal-search"
             id="icdSearch"
             placeholder="ICD-Code oder Diagnosetext suchen..."
             autocomplete="off">

      <ul class="diag-modal-list" id="icdList"></ul>

      <div class="diag-modal-footer">
        <button type="button" class="diag-modal-cancel" id="icdModalCancel">Abbrechen</button>
      </div>
    </div>
  </div>
</div>
<script>
(function(){
  document.querySelectorAll('.diag-group-header').forEach(function(header){
    header.addEventListener('click',function(){
      var group=header.closest('.diag-group');
      if(group){group.classList.toggle('open');}
    });
  });

  function updateDiagnosisCount(list){
    if(!list){return;}

    var group=list.closest('.diag-group');
    if(!group){return;}

    var header=group.querySelector('.diag-group-header');
    if(!header){return;}

    var title=header.getAttribute('data-title') || header.textContent.replace(/\s*\(\d+\)\s*$/,'').trim();
    header.setAttribute('data-title',title);

    var count=list.querySelectorAll(':scope > tr').length;
    header.textContent=title+' ('+count+')';
  }

  document.querySelectorAll('.diag-list[data-mcdms-record-list]').forEach(function(list){
    updateDiagnosisCount(list);

    new MutationObserver(function(){
      updateDiagnosisCount(list);
    }).observe(list,{childList:true});
  });

  function updateDateEmptyState(input){
    if(!input || !input.matches('.mcdms-diagnosen input[type="date"][data-record-field="DiagDatum"]')){
      return;
    }

    if(input.value){
      input.classList.remove('date-empty');
    }else{
      input.classList.add('date-empty');
    }
  }

  document.querySelectorAll('.mcdms-diagnosen input[type="date"][data-record-field="DiagDatum"]').forEach(updateDateEmptyState);

  if(!window.__mcdmsDiagnosenDateObserverBound){
    window.__mcdmsDiagnosenDateObserverBound=true;

    new MutationObserver(function(mutations){
      mutations.forEach(function(mutation){
        mutation.addedNodes.forEach(function(node){
          if(node.nodeType!==1){return;}

          if(node.matches && node.matches('.mcdms-diagnosen input[type="date"][data-record-field="DiagDatum"]')){
            updateDateEmptyState(node);
          }

          if(node.querySelectorAll){
            node.querySelectorAll('.mcdms-diagnosen input[type="date"][data-record-field="DiagDatum"]').forEach(updateDateEmptyState);
          }
        });
      });
    }).observe(document.body,{childList:true,subtree:true});

    document.addEventListener('change',function(event){
      updateDateEmptyState(event.target);
    },true);

    document.addEventListener('input',function(event){
      updateDateEmptyState(event.target);
    },true);
  }

  if(!window.__mcdmsDiagnosenDateFocusBound){
    window.__mcdmsDiagnosenDateFocusBound=true;

    document.addEventListener('focusin',function(event){
      var input=event.target;

      if(!input.matches('.mcdms-diagnosen input[type="date"][data-record-field="DiagDatum"]')){
        return;
      }

      if(input.value){
        return;
      }

      var now=new Date();
      var year=now.getFullYear();
      var month=String(now.getMonth()+1).padStart(2,'0');
      var day=String(now.getDate()).padStart(2,'0');

      input.value=year+'-'+month+'-'+day;
      input.classList.remove('date-empty');
      input.dispatchEvent(new Event('input',{bubbles:true}));
      input.dispatchEvent(new Event('change',{bubbles:true}));
    },true);
  }

  var icdDataElement=document.getElementById('icd-data');
  var icdRaw=icdDataElement ? icdDataElement.textContent : '';
  var icdEntries=icdRaw.split(/\r?\n/).map(function(line){
    line=line.trim();
    if(!line){return null;}

    var pos=line.indexOf('|');
    if(pos<0){return null;}

    var code=line.substring(0,pos).trim();
    var textValue=line.substring(pos+1).trim();

    if(!code && !textValue){return null;}

    return {
      code:code,
      text:textValue,
      search:(code+' '+textValue).toLowerCase()
    };
  }).filter(function(entry){return entry!==null;});

  var icdModal=document.getElementById('icdModal');
  var icdSearch=document.getElementById('icdSearch');
  var icdList=document.getElementById('icdList');
  var icdModalClose=document.getElementById('icdModalClose');
  var icdModalCancel=document.getElementById('icdModalCancel');
  var activeDiagnosisRow=null;

  function renderIcdEntries(entries){
    if(!icdList){return;}

    icdList.innerHTML='';

    var visible=entries.slice(0,100);

    if(visible.length===0){
      var empty=document.createElement('li');
      empty.className='diag-modal-empty';
      empty.textContent='Keine Diagnosen gefunden.';
      icdList.appendChild(empty);
      return;
    }

    visible.forEach(function(entry){
      var li=document.createElement('li');
      li.className='diag-modal-item';
      li.setAttribute('data-icd-code',entry.code);
      li.setAttribute('data-icd-text',entry.text);

      var codeSpan=document.createElement('span');
      codeSpan.className='diag-modal-code';
      codeSpan.textContent=entry.code;

      var textSpan=document.createElement('span');
      textSpan.className='diag-modal-text';
      textSpan.textContent=entry.text;

      li.appendChild(codeSpan);
      li.appendChild(textSpan);
      icdList.appendChild(li);
    });
  }

  function filterIcdEntries(){
    var query=(icdSearch ? icdSearch.value : '').toLowerCase().trim();

    if(!query){
      renderIcdEntries(icdEntries.slice(0,100));
      return;
    }

    var parts=query.split(/\s+/).filter(function(part){return part.length>0;});
    var found=icdEntries.filter(function(entry){
      return parts.every(function(part){
        return entry.search.indexOf(part)>=0;
      });
    });

    renderIcdEntries(found);
  }

  function openIcdModal(button){
    activeDiagnosisRow=button.closest('tr');
    if(!activeDiagnosisRow || !icdModal || !icdSearch){return;}

    icdSearch.value='';
    renderIcdEntries(icdEntries.slice(0,100));
    icdModal.style.display='flex';

    setTimeout(function(){
      icdSearch.focus();
    },0);
  }

  function closeIcdModal(){
    if(icdModal){icdModal.style.display='none';}
    activeDiagnosisRow=null;
  }

  document.addEventListener('click',function(event){
    var selectButton=event.target.closest('[data-icd-select]');
    if(selectButton && selectButton.closest('.mcdms-diagnosen')){
      event.preventDefault();
      openIcdModal(selectButton);
      return;
    }

    var item=event.target.closest('.diag-modal-item');
    if(item && activeDiagnosisRow){
      var diagnosisInput=activeDiagnosisRow.querySelector('[data-record-field="Diagnose"]');
      var icdInput=activeDiagnosisRow.querySelector('[data-record-field="ICDNR"]');

      if(diagnosisInput){
        diagnosisInput.value=item.getAttribute('data-icd-text') || '';
        diagnosisInput.dispatchEvent(new Event('input',{bubbles:true}));
        diagnosisInput.dispatchEvent(new Event('change',{bubbles:true}));
      }

      if(icdInput){
        icdInput.value=item.getAttribute('data-icd-code') || '';
        icdInput.dispatchEvent(new Event('input',{bubbles:true}));
        icdInput.dispatchEvent(new Event('change',{bubbles:true}));
      }

      closeIcdModal();
      return;
    }

    if(event.target===icdModal){
      closeIcdModal();
    }
  });

  if(icdSearch){
    icdSearch.addEventListener('input',filterIcdEntries);
  }

  if(icdModalClose){
    icdModalClose.addEventListener('click',closeIcdModal);
  }

  if(icdModalCancel){
    icdModalCancel.addEventListener('click',closeIcdModal);
  }

  document.addEventListener('keydown',function(event){
    if(event.key==='Escape' && icdModal && icdModal.style.display==='flex'){
      closeIcdModal();
    }
  });

  if(!window.__mcdmsDiagnosenDeleteConfirmBound){
    window.__mcdmsDiagnosenDeleteConfirmBound=true;
    document.addEventListener('click',function(event){
      var button=event.target.closest('[data-mcdms-record-delete]');
      if(!button || !button.closest('.mcdms-diagnosen')){return;}

      if(!window.confirm('Möchten Sie diese Zeile wirklich löschen?')){
        event.preventDefault();
        event.stopImmediatePropagation();
      }
    },true);
  }
})();
</script>
</body>
</html>

Dieses Beispiel zeigt damit gemeinsam:

RecordView=LIST
mehrere fachliche Bereiche
DIAGTYP
ICDRANG
persistente Sortierung
Hinzufügen
Ändern
Duplizieren
Löschen
Verschieben
ICD-Suche
ScriptSaveHTML
responsive Tabelle

3. Medikationsplan

Ziel

Der Medikationsplan ist ein umfangreiches Master-/Detailbeispiel.

Allgemeine Angaben gelten für die gesamte Verordnung und werden im Master gespeichert.

Die einzelnen Medikamente werden als Detaildatensätze gespeichert.

Masterdaten

Beispielsweise:

THROMBOSE
MEDIKAMENTENDOSE
BTM
ALLERGIE
ALLERGIE_TEXT
ANGABEN_VERORDNUNG

Zusätzlich können Auditinformationen gespeichert werden:

ErstelltVon
ErstelltAm
DATE

Detaildaten

Eine Medikamentenzeile kann beispielsweise enthalten:

BEREICH
VOS
VDATUM
BEDARF
NAMEWIRKSTOFF
SCHEMA
AUSGABE
DAUER
GRUND

Datenmodell

Database=FRM_MEDIKATIONSPLAN;FRMID;PatientID#FRM_MEDIKATIONSPLAN_DETAIL;ID;FRMID;PatientID

Damit existieren:

1 Masterdatensatz
n Detaildatensätze

pro Medikationsplan.

Sichtbare Medikamentenbereiche

Das Formular zeigt die Medikamente in drei Bereichen:

Regelmedikation

Zu besonderen Zeiten anzuwendende Medikamente

Selbstmedikation

Alle drei Bereiche verwenden dieselbe Detailtabelle.

Die Zuordnung erfolgt über:

BEREICH

mit beispielsweise:

REGEL
BESONDERE_ZEITEN
SELBST

Mehrere Detailcontainer

Das HTML enthält drei echte Detailcontainer:

<tbody
    data-mcdms-detail-list
    data-med-area-list="REGEL">
</tbody>
<tbody
    data-mcdms-detail-list
    data-med-area-list="BESONDERE_ZEITEN">
</tbody>
<tbody
    data-mcdms-detail-list
    data-med-area-list="SELBST">
</tbody>

Es gibt trotzdem nur:

eine Detailtabelle
ein Detailmodell
ein gemeinsames Detailtemplate

Die WebAkte sammelt beim Speichern alle vorhandenen data-mcdms-detail-list-Container ein. app

Masterbereich

Im oberen Bereich des Formulars können beispielsweise angezeigt bzw. bearbeitet werden:

Patient
Diagnosen
Verordnung
Allergien
Angaben zu dieser Verordnung

Patient und Diagnosen können aus MCDMS- bzw. Scriptvariablen stammen und müssen deshalb nicht zwingend selbst in der Medikationsplantabelle gespeichert werden.

Bedienung

Je Bereich können Medikamente:

  • hinzugefügt
  • bearbeitet
  • verschoben
  • dupliziert
  • gelöscht

werden.

Neue Medikamentenzeilen erhalten automatisch den zugehörigen BEREICH.

MAIN.HTML
Copy
<!DOCTYPE html>
<html lang="de">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Medikamentenliste</title>
  <style>
    body {
      margin: 0;
      padding: 0;
      background: transparent;
    }

    .table-container {
      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
    }

    .table-container {
      width: 100%;
      overflow-x: auto;
      background-color: #ffffff;
      box-shadow: 0 1px 3px rgba(0,0,0,0.05);
      border-radius: 8px;
    }

    .med-tabelle {
      width: 100%;
      border-collapse: collapse;
    }

    .med-tabelle th {
      background-color: #f0f4f8;
      color: #334155;
      font-size: 13px;
      font-weight: 600;
      padding: 12px 8px;
      border: 1px solid #d9e2ec;
    }

    .med-tabelle th small {
      display: block;
      color: #627d98;
      font-weight: normal;
      margin-top: 2px;
    }

    .med-tabelle td {
      padding: 10px 8px;
      border: 1px solid #e2e8f0;
      vertical-align: middle;
      text-align: center;
    }

    .search-wrapper {
      display: flex;
      align-items: center;
      gap: 6px;
      width: 100%;
    }

    .med-tabelle input[type="text"] {
      width: 100%;
      box-sizing: border-box;
      border: 1px solid #cbd5e1;
      border-radius: 8px;
      padding: 8px 12px;
      font-size: 14px;
      color: #334155;
      outline: none;
      background-color: #fff;
    }

    .btn-select-trigger {
      background: #f1f5f9;
      border: 1px solid #cbd5e1;
      border-radius: 6px;
      color: #475569;
      cursor: pointer;
      padding: 7px 10px;
      font-size: 14px;
      transition: all 0.2s;
      flex-shrink: 0;
    }

    .btn-select-trigger:hover {
      background: #e2e8f0;
      color: #1e293b;
    }

    .med-tabelle input[type="checkbox"] {
      appearance: none;
      -webkit-appearance: none;
      width: 20px;
      height: 20px;
      cursor: pointer;
      background: #ffffff;
      border: 1px solid #cbd5e1;
      border-radius: 4px;
      display: inline-grid;
      place-content: center;
      vertical-align: middle;
    }

    .med-tabelle input[type="checkbox"]::before {
      content: "";
      width: 10px;
      height: 10px;
      transform: scale(0);
      transition: transform 0.08s ease-in-out;
      clip-path: polygon(14% 44%, 0 65%, 43% 100%, 100% 16%, 80% 0, 40% 62%);
      background: #ffffff;
    }

    .med-tabelle input[type="checkbox"]:checked {
      background: #2b6cb0;
      border-color: #2b6cb0;
    }

    .med-tabelle input[type="checkbox"]:checked::before {
      transform: scale(1);
    }

    .med-tabelle input[type="checkbox"]:focus {
      outline: none;
      box-shadow: 0 0 0 3px rgba(43,108,176,.15);
    }

    /* Actions Column Buttons */
    .actions-cell {
      display: flex;
      gap: 8px;
      justify-content: center;
      align-items: center;
    }

    .btn-action {
      background: none;
      border: none;
      font-size: 16px;
      cursor: pointer;
      padding: 4px;
      transition: color 0.2s;
    }

    .btn-move {
      color: #64748b;
    }

    .btn-move:hover {
      color: #334155;
    }

    .btn-duplicate {
      color: #3b82f6;
    }

    .btn-duplicate:hover {
      color: #1d4ed8;
    }

    .btn-delete {
      color: #ef4444;
    }

    .btn-delete:hover {
      color: #b91c1c;
    }

    .button-bar-bottom {
      padding: 12px;
      background-color: #f8fafc;
      border-top: 1px solid #e2e8f0;
      display: flex;
      justify-content: center;
    }

    .btn {
      padding: 10px 20px;
      border-radius: 6px;
      font-size: 14px;
      font-weight: 500;
      cursor: pointer;
      border: 1px solid #bfdbfe;
      background-color: #eff6ff;
      color: #2563eb;
      transition: all 0.2s;
    }

    .btn:hover {
      background-color: #dbeafe;
    }

    /* MODAL DESIGN */
    .modal-backdrop {
      display: none;
      position: fixed;
      top: 0; left: 0; width: 100%; height: 100%;
      background: rgba(0, 0, 0, 0.4);
      z-index: 1000;
      justify-content: center;
      align-items: center;
    }

    .modal-content {
      background: #ffffff;
      color: #1e293b;
      padding: 20px;
      border-radius: 12px;
      width: 90%;
      max-width: 400px;
      box-shadow: 0 4px 12px rgba(0,0,0,0.15);
    }

    #dialogSuche {
      background: #ffffff !important;
      color: #334155 !important;
    }

    #dialogSuche::placeholder {
      color: #94a3b8;
      opacity: 1;
    }

    #dialogListe li {
      background: #ffffff;
      color: #334155;
    }

    .modal-header {
      font-size: 16px;
      font-weight: 600;
      margin-bottom: 15px;
      color: #1e293b;
      display: flex;
      justify-content: space-between;
      align-items: center;
    }

    .modal-close {
      background: none;
      border: none;
      font-size: 18px;
      cursor: pointer;
      color: #94a3b8;
    }

    .med-list-item {
      padding: 10px;
      border: 1px solid #e2e8f0;
      border-radius: 6px;
      margin-bottom: 8px;
      cursor: pointer;
      transition: background 0.2s;
    }

    .med-list-item:hover {
      background: #f0fdf4;
      border-color: #bbf7d0;
    }


    .med-top-grid {
      display: grid;
      grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
      gap: 14px;
      margin-bottom: 14px;
      align-items: stretch;
    }

    .med-top-grid .med-page-head {
      margin-bottom: 0;
      height: 100%;
      box-sizing: border-box;
    }

    .med-diagnosen-card {
      height: 100%;
      box-sizing: border-box;
    }

    .med-page-head {
      background: #ffffff;
      border-radius: 14px;
      padding: 22px 26px;
      margin-bottom: 14px;
      display: flex;
      justify-content: space-between;
      align-items: center;
      gap: 20px;
      border-left: 6px solid #2b6cb0;
      box-shadow: 0 2px 8px rgba(0,0,0,0.04);
      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
    }

    .med-page-head-left h1 {
      margin: 0;
      font-size: 28px;
      line-height: 1.15;
      font-weight: 700;
      color: #24364b;
    }

    .med-page-head-sub {
      margin-top: 6px;
      font-size: 16px;
      color: #6b7a90;
      font-weight: 600;
    }

    .med-page-head-badge {
      background: #eef4fb;
      color: #2b6cb0;
      padding: 10px 18px;
      border-radius: 999px;
      font-size: 14px;
      font-weight: 600;
      white-space: nowrap;
    }


    /* Allgemeine Angaben zum gesamten Medikationsplan (Master) */
    .med-master {
      display: grid;
      gap: 14px;
      margin-bottom: 20px;
      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
    }

    .med-master-grid {
      display: grid;
      grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
      gap: 14px;
    }

    .med-master-card {
      background: #ffffff;
      border: 1px solid #d9e2ec;
      border-radius: 8px;
      padding: 14px;
      box-shadow: 0 1px 3px rgba(0,0,0,0.04);
      min-width: 0;
    }

    .med-master-title {
      margin: 0 0 10px;
      font-size: 15px;
      font-weight: 700;
      color: #24364b;
    }

    .med-master-info {
      display: grid;
      gap: 6px;
      font-size: 14px;
      color: #334155;
      line-height: 1.45;
    }

    .med-diagnosen-text {
      white-space: pre-line;
    }

    .med-master-options {
      display: flex;
      flex-wrap: wrap;
      align-items: center;
      gap: 12px 24px;
    }

    .med-master-check {
      display: inline-flex;
      align-items: center;
      gap: 8px;
      cursor: pointer;
      color: #334155;
      font-size: 14px;
    }

    .med-master-check input[type="checkbox"] {
      appearance: none;
      -webkit-appearance: none;
      width: 20px;
      height: 20px;
      margin: 0;
      cursor: pointer;
      background: #ffffff;
      border: 1px solid #cbd5e1;
      border-radius: 4px;
      display: inline-grid;
      place-content: center;
      flex: 0 0 auto;
    }

    .med-master-check input[type="checkbox"]::before {
      content: "";
      width: 10px;
      height: 10px;
      transform: scale(0);
      transition: transform 0.08s ease-in-out;
      clip-path: polygon(14% 44%, 0 65%, 43% 100%, 100% 16%, 80% 0, 40% 62%);
      background: #ffffff;
    }

    .med-master-check input[type="checkbox"]:checked {
      background: #2b6cb0;
      border-color: #2b6cb0;
    }

    .med-master-check input[type="checkbox"]:checked::before {
      transform: scale(1);
    }

    .med-master-allergy {
      display: grid;
      grid-template-columns: auto minmax(180px, 1fr);
      align-items: center;
      gap: 10px;
    }

    .med-master input[type="text"],
    .med-master textarea {
      width: 100%;
      box-sizing: border-box;
      border: 1px solid #cbd5e1;
      border-radius: 8px;
      padding: 9px 12px;
      font: inherit;
      font-size: 14px;
      color: #334155;
      background: #ffffff;
      outline: none;
    }

    .med-master textarea {
      min-height: 88px;
      resize: vertical;
    }

    .med-master input[type="text"]:focus,
    .med-master textarea:focus {
      border-color: #93b4d1;
      box-shadow: 0 0 0 3px rgba(43,108,176,.10);
    }

    .med-master-note {
      margin-top: 8px;
      font-size: 12px;
      line-height: 1.4;
      color: #6b7a90;
    }

    /* Vollständig getrennte Medikationsbereiche */
    .med-sections {
      display: grid;
      gap: 24px;
    }

    .med-section {
      min-width: 0;
    }

    .med-section-title {
      margin: 0;
      padding: 10px 14px;
      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
      font-size: 16px;
      font-weight: 700;
      background-color: var(--title-bg, #ffffff);
      color: var(--title-color, #24364b);
      border-bottom: 1px solid #e2e8f0;
    }

    .med-section .table-container {
      background-color: #ffffff;
      box-shadow: 0 1px 3px rgba(0,0,0,0.05);
      border-radius: 8px;
      overflow: hidden;
    }

    .med-section .button-bar-bottom .btn {
      width: auto;
    }

    /* Desktop column widths */
    .col-v, .col-b, .col-ausgabe { width: 50px; }
    .col-datum { width: 140px; }
    .col-dosierung { width: 140px; }
    .col-dauer { width: 90px; }
    .col-action { width: 80px; }

    @media print {
      .mcdms-form-scope {
        background: #fff;
        padding: 0;
      }

      .mcdms-form-scope .med-section {
        box-shadow: none;
      }
    }

    @media screen and (max-width: 768px) {
      .med-top-grid {
        grid-template-columns: 1fr;
      }

      .med-page-head {
        padding: 18px 16px;
        flex-direction: column;
        align-items: flex-start;
        gap: 12px;
        border-radius: 12px;
      }

      .med-page-head-left h1 {
        font-size: 22px;
      }

      .med-page-head-sub {
        font-size: 14px;
      }

      .med-page-head-badge {
        padding: 8px 14px;
        font-size: 13px;
      }

      .med-master-grid {
        grid-template-columns: 1fr;
      }

      .med-master-allergy {
        grid-template-columns: 1fr;
      }

      .med-sections { gap: 20px; }
      .med-section .table-container {
        background: #ffffff;
        box-shadow: 0 1px 3px rgba(0,0,0,0.05);
      }
      .med-tabelle thead { display: none; }
      .med-tabelle tbody tr {
        display: block; background: #ffffff; border: 1px solid #cbd5e1;
        border-radius: 12px; margin-bottom: 15px; padding: 15px; position: relative;
      }
      .med-tabelle td { display: flex; align-items: center; justify-content: space-between; border: none; padding: 8px 0; }
      .med-tabelle td::before { content: attr(data-label); font-weight: 600; font-size: 13px; color: #475569; width: 40%; text-align: left; }
      .med-tabelle input[type="text"], .search-wrapper { width: 60%; }
      .med-tabelle input[type="checkbox"] { margin-right: auto; }
      .med-tabelle td.cell-action { position: absolute; top: 10px; right: 10px; width: auto; padding: 0; }
      .med-tabelle td.cell-action::before { display: none; }
      .button-bar-bottom { background: #ffffff; border-top: 1px solid #e2e8f0; padding: 10px 12px; }
      .btn { width: 100%; text-align: center; }
    }
  </style>
</head>
<body>

  <div class="med-top-grid">
    <section class="med-page-head">
      <div class="med-page-head-left">
        <div class="med-page-head-sub"><h2>Medikationsplan</h2></div>
        <div class="med-page-head-sub">[#GESAMTNAME#]</div>
        <div class="med-page-head-sub">Geburtsdatum [#GEBDATUM#]</div>
      </div>
      <div class="med-page-head-badge">
        Medikation & Verordnung
      </div>
    </section>

    <section class="med-master-card med-diagnosen-card">
      <h2 class="med-master-title">Diagnosen</h2>
      <div class="med-master-info med-diagnosen-text">[#DIAGNOSEGESAMT#]</div>
    </section>
  </div>
<input type="hidden"
       name="ErstelltVon"
       data-field="ErstelltVon"
       value="[#ErstelltVon#]">

<input type="hidden"
       name="ErstelltAm"
       data-field="ErstelltAm"
       value="[#ErstelltAm#]">

<input type="hidden"
       name="DATE"
       data-field="DATE"
       value="[#DATE#]">

  <div id="hausliste-data" hidden>[#HAUSLISTE#]</div>

  <div class="med-master">
    <div class="med-master-grid">
      <section class="med-master-card">
        <h2 class="med-master-title">Verordnung</h2>
        <div class="med-master-options">
          <label class="med-master-check">
            <input type="checkbox"
                   name="THROMBOSE"
                   data-field="THROMBOSE">
            <span>Thromboseprophylaxe s.c. durch Pflegedienst</span>
          </label>

          <label class="med-master-check">
            <input type="checkbox"
                   name="MEDIKAMENTENDOSE"
                   data-field="MEDIKAMENTENDOSE">
            <span>Medikamentendosette</span>
          </label>

          <label class="med-master-check">
            <input type="checkbox"
                   name="BTM"
                   data-field="BTM">
            <span>BTM</span>
          </label>
        </div>
      </section>

      <section class="med-master-card">
        <h2 class="med-master-title">Allergien</h2>
        <div class="med-master-allergy">
          <label class="med-master-check">
            <input type="checkbox"
                   name="ALLERGIE"
                   data-field="ALLERGIE">
            <span>Allergie bekannt</span>
          </label>

          <input type="text"
                 name="ALLERGIE_TEXT"
                 data-field="ALLERGIE_TEXT"
                 value="[#ALLERGIE_TEXT#]"
                 placeholder="Allergien / Unverträglichkeiten">
        </div>
      </section>
    </div>

    <section class="med-master-card">
      <h2 class="med-master-title">Angaben zu dieser Verordnung</h2>
      <textarea name="ANGABEN_VERORDNUNG"
                data-field="ANGABEN_VERORDNUNG"
                placeholder="Freitext zur gesamten Verordnung">[#ANGABEN_VERORDNUNG#]</textarea>
      <div class="med-master-note">
        Soweit bei „Hinweis / Grund“ nicht anders benannt, können selbstverständlicherweise auch Äquivalenzpräparate anderer Hersteller rezeptiert werden.
      </div>
    </section>
  </div>

  <div class="med-sections" data-medication-plan>
    <section class="med-section" data-med-section="REGEL"
             style="--title-bg:#dbeafe; --title-color:#1e3a8a;">
      <div class="table-container">
        <h2 class="med-section-title">Regelmedikation</h2>
        <table class="med-tabelle">
          <thead>
            <tr>
              <th class="col-v">V</th>
              <th class="col-datum">Datum</th>
              <th class="col-b">B</th>
              <th>Name, Wirkstoff, Stärke, Form</th>
              <th class="col-dosierung">Dosierung<small>z. B. 1-0-1-0</small></th>
              <th class="col-ausgabe">Ausgabe</th>
              <th class="col-dauer">Dauer</th>
              <th>Hinweis / Grund</th>
              <th class="col-action">Aktionen</th>
            </tr>
          </thead>
          <tbody data-med-area-list="REGEL" data-mcdms-detail-list>
            <!-- Der Server rendert alle Detailzeilen zunächst hier hinein. -->
          </tbody>
        </table>
        <div class="button-bar-bottom">
          <button type="button" class="btn" data-add-med-area="REGEL">+ Zeile hinzufügen</button>
        </div>
      </div>
    </section>

    <section class="med-section" data-med-section="BESONDERE_ZEITEN"
             style="--title-bg:#fef3c7; --title-color:#92400e;">
      <div class="table-container">
        <h2 class="med-section-title">Zu besonderen Zeiten anzuwendende Medikamente</h2>
        <table class="med-tabelle">
          <thead>
            <tr>
              <th class="col-v">V</th>
              <th class="col-datum">Datum</th>
              <th class="col-b">B</th>
              <th>Name, Wirkstoff, Stärke, Form</th>
              <th class="col-dosierung">Dosierung<small>z. B. 1-0-1-0</small></th>
              <th class="col-ausgabe">Ausgabe</th>
              <th class="col-dauer">Dauer</th>
              <th>Hinweis / Grund</th>
              <th class="col-action">Aktionen</th>
            </tr>
          </thead>
          <tbody data-mcdms-detail-list data-med-area-list="BESONDERE_ZEITEN"></tbody>
        </table>
        <div class="button-bar-bottom">
          <button type="button" class="btn" data-add-med-area="BESONDERE_ZEITEN">+ Zeile hinzufügen</button>
        </div>
      </div>
    </section>

    <section class="med-section" data-med-section="SELBST"
             style="--title-bg:#dcfce7; --title-color:#166534;">
      <div class="table-container">
        <h2 class="med-section-title">Selbstmedikation</h2>
        <table class="med-tabelle">
          <thead>
            <tr>
              <th class="col-v">V</th>
              <th class="col-datum">Datum</th>
              <th class="col-b">B</th>
              <th>Name, Wirkstoff, Stärke, Form</th>
              <th class="col-dosierung">Dosierung<small>z. B. 1-0-1-0</small></th>
              <th class="col-ausgabe">Ausgabe</th>
              <th class="col-dauer">Dauer</th>
              <th>Hinweis / Grund</th>
              <th class="col-action">Aktionen</th>
            </tr>
          </thead>
          <tbody data-mcdms-detail-list data-med-area-list="SELBST"></tbody>
        </table>
        <div class="button-bar-bottom">
          <button type="button" class="btn" data-add-med-area="SELBST">+ Zeile hinzufügen</button>
        </div>
      </div>
    </section>
  </div>

  <template data-mcdms-detail-row>
    <tr>
      <td data-label="V">
        <input type="hidden" data-detail-field="BEREICH" value="">
        <input type="checkbox" data-detail-field="VOS">
      </td>
      <td data-label="Datum"><input type="text" data-detail-field="VDATUM"></td>
      <td data-label="B"><input type="checkbox" data-detail-field="BEDARF"></td>
      <td data-label="Name, Wirkstoff...">
        <div class="search-wrapper">
          <input type="text" data-detail-field="NAMEWIRKSTOFF">
          <button type="button" class="btn-select-trigger" onclick="openSelection(this)">🔍</button>
        </div>
      </td>
      <td data-label="Dosierung"><input type="text" data-detail-field="SCHEMA"></td>
      <td data-label="Ausgabe"><input type="checkbox" data-detail-field="AUSGABE"></td>
      <td data-label="Dauer"><input type="text" data-detail-field="DAUER"></td>
      <td data-label="Hinweis / Grund"><input type="text" data-detail-field="GRUND"></td>
      <td class="cell-action">
        <div class="actions-cell">
          <button type="button" class="btn-action btn-move" title="Nach oben verschieben" onclick="moveRow(this, 'up')">▲</button>
          <button type="button" class="btn-action btn-move" title="Nach unten verschieben" onclick="moveRow(this, 'down')">▼</button>
          <button type="button" class="btn-action btn-duplicate" title="Zeile duplizieren" onclick="duplicateRow(this)">📋</button>
          <button type="button" class="btn-action btn-delete" title="Zeile löschen" onclick="deleteRow(this)">✕</button>
        </div>
      </td>
    </tr>
  </template>

  <!-- AUSWAHL-MODAL -->
  <div class="modal-backdrop" id="medModal">
    <div class="modal-content">
      <div class="modal-header">
        <span>Eintrag auswählen</span>
        <button type="button" class="modal-close" id="closeDialogBtn">✕</button>
      </div>

      <input type="text"
             id="dialogSuche"
             placeholder="Suchbegriff eingeben..."
             style="width:100%; padding:10px; margin-bottom:15px; font-size:14px; border:1px solid #aaa; border-radius:4px; box-sizing:border-box;">

      <ul id="dialogListe"
          style="list-style:none; padding:0; margin:15px 0; max-height:250px; overflow-y:auto;"></ul>

      <button type="button"
              class="btn"
              id="cancelDialogBtn"
              style="width:100%; margin-top:10px; background:#f1f5f9; color:#334155; border-color:#cbd5e1;">
        Abbrechen
      </button>
    </div>
  </div>

  <script>
    (function() {
      const runtimeKey = '__mcdmsMedicationPlanRuntime';
      const existingRuntime = window[runtimeKey];

      if (existingRuntime && typeof existingRuntime.refresh === 'function') {
        existingRuntime.refresh();
        return;
      }

      const medSections = [
        { key: 'REGEL' },
        { key: 'BESONDERE_ZEITEN' },
        { key: 'SELBST' }
      ];

      const runtime = {
        activeInputTarget: null,
        refreshing: false,
        refresh: null,
        observer: null
      };

      window[runtimeKey] = runtime;

      function getMedicationPlan() {
        return document.querySelector('[data-medication-plan]');
      }

      function getDetailTemplate() {
        return document.querySelector('template[data-mcdms-detail-row]');
      }

      function normalizeArea(value) {
        const normalized = String(value || '').trim().toUpperCase();
        return medSections.some(section => section.key === normalized)
          ? normalized
          : 'REGEL';
      }

      function getAreaList(plan, area) {
        if (!plan) return null;
        return plan.querySelector(
          '[data-med-area-list="' + normalizeArea(area) + '"]'
        );
      }

      function isMedicationRow(row) {
        return !!(
          row &&
          row.matches &&
          row.matches('tr') &&
          row.querySelector('[data-detail-field]')
        );
      }

      function medicationRows(plan) {
        if (!plan) return [];

        return Array.from(
          plan.querySelectorAll('[data-med-area-list] > tr')
        ).filter(isMedicationRow);
      }

      function rowArea(row) {
        const field = row && row.querySelector('[data-detail-field="BEREICH"]');

        if (field && String(field.value || '').trim() !== '') {
          return normalizeArea(field.value);
        }

        return 'REGEL';
      }

      function rowHasMedicationData(row) {
        if (!row) return false;

        return Array.from(row.querySelectorAll('[data-detail-field]')).some(control => {
          const fieldName = String(
            control.getAttribute('data-detail-field') || ''
          ).trim().toUpperCase();

          if (fieldName === 'BEREICH') return false;

          if (control.type === 'checkbox' || control.type === 'radio') {
            return control.checked === true;
          }

          return String(control.value || '').trim() !== '';
        });
      }

      function ensureRowArea(row) {
        if (!row) return;

        const field = row.querySelector('[data-detail-field="BEREICH"]');
        if (!field || String(field.value || '').trim() !== '') return;
        if (!rowHasMedicationData(row)) return;

        const list = row.closest('[data-med-area-list]');
        field.value = normalizeArea(
          list && list.getAttribute('data-med-area-list')
        );
        field.dispatchEvent(new Event('input', { bubbles: true }));
        field.dispatchEvent(new Event('change', { bubbles: true }));
      }

      function organizeMedicationRows() {
        if (runtime.refreshing) return;

        const plan = getMedicationPlan();
        if (!plan) return;

        runtime.refreshing = true;

        try {
          const lists = Array.from(
            plan.querySelectorAll('[data-med-area-list]')
          );

          if (lists.length !== medSections.length) return;

          medicationRows(plan).forEach(row => {
            const area = rowArea(row);
            const target = getAreaList(plan, area);

            row.dataset.medArea = area;
            if (target && row.parentElement !== target) {
              target.appendChild(row);
            }
          });

          plan.dataset.medicationPlanReady = 'true';
        } finally {
          runtime.refreshing = false;
        }
      }

      runtime.refresh = organizeMedicationRows;

      function createBlankRow(area) {
        const template = getDetailTemplate();
        if (!template || !template.content) return null;

        const templateRow = template.content.querySelector('tr');
        if (!templateRow) return null;

        const row = templateRow.cloneNode(true);
        const normalizedArea = normalizeArea(area);
        const field = row.querySelector('[data-detail-field="BEREICH"]');

        row.dataset.medArea = normalizedArea;
        if (field) field.value = normalizedArea;

        return row;
      }

      function addDetailRow(area) {
        const plan = getMedicationPlan();
        const normalizedArea = normalizeArea(area);
        const list = getAreaList(plan, normalizedArea);
        const row = createBlankRow(normalizedArea);

        if (!list || !row) return null;

        list.appendChild(row);
        return row;
      }

      function controlsInRow(row) {
        return row
          ? row.querySelectorAll('input, select, textarea')
          : [];
      }

      window.moveRow = function(button, direction) {
        const row = button && button.closest('tr');
        const list = row && row.closest('[data-med-area-list]');

        if (!isMedicationRow(row) || !list) return;

        const rows = Array.from(list.children).filter(isMedicationRow);
        const index = rows.indexOf(row);

        if (direction === 'up' && index > 0) {
          list.insertBefore(row, rows[index - 1]);
          return;
        }

        if (direction === 'down' && index >= 0 && index < rows.length - 1) {
          list.insertBefore(rows[index + 1], row);
        }
      };

      window.duplicateRow = function(button) {
        const currentRow = button && button.closest('tr');
        const list = currentRow && currentRow.closest('[data-med-area-list]');

        if (!isMedicationRow(currentRow) || !list) return;

        const area = normalizeArea(list.getAttribute('data-med-area-list'));
        const clonedRow = currentRow.cloneNode(true);
        const originalControls = controlsInRow(currentRow);
        const clonedControls = controlsInRow(clonedRow);

        originalControls.forEach((control, index) => {
          const clonedControl = clonedControls[index];
          if (!clonedControl) return;

          if (control.type === 'checkbox' || control.type === 'radio') {
            clonedControl.checked = control.checked;
          } else {
            clonedControl.value = control.value;
          }
        });

        clonedRow.dataset.medArea = area;

        const areaField = clonedRow.querySelector('[data-detail-field="BEREICH"]');
        if (areaField) areaField.value = area;

        list.insertBefore(clonedRow, currentRow.nextSibling);
      };

      window.deleteRow = function(button) {
        const row = button && button.closest('tr');
        if (!isMedicationRow(row)) return;

        let hasContent = false;

        row.querySelectorAll('[data-detail-field]').forEach(control => {
          const fieldName = String(
            control.getAttribute('data-detail-field') || ''
          ).trim().toUpperCase();

          if (fieldName === 'BEREICH') return;

          if (control.type === 'checkbox' || control.type === 'radio') {
            if (control.checked) hasContent = true;
          } else if (String(control.value || '').trim() !== '') {
            hasContent = true;
          }
        });

        if (!hasContent || confirm('Möchten Sie diese Zeile wirklich löschen?')) {
          row.remove();
        }
      };

      function medicationValues() {
        const dataElement = document.getElementById('hausliste-data');
        const rawValue = dataElement ? dataElement.textContent : '';

        return rawValue.split(/\r?\n/)
          .map(item => item.trim())
          .filter(item => item.length > 0);
      }

      function showResults(results) {
        const list = document.getElementById('dialogListe');
        if (!list) return;

        list.replaceChildren();

        const visibleResults = results.slice(0, 30);

        if (visibleResults.length === 0) {
          const empty = document.createElement('li');
          empty.style.cssText = 'padding:10px; color:#999; font-style:italic;';
          empty.textContent = 'Keine Einträge gefunden...';
          list.appendChild(empty);
          return;
        }

        visibleResults.forEach(value => {
          const item = document.createElement('li');
          item.className = 'med-list-item';
          item.setAttribute('data-wert', value);
          item.textContent = value;
          list.appendChild(item);
        });
      }

      function closeModal() {
        const dialog = document.getElementById('medModal');
        if (dialog) dialog.style.display = 'none';
        runtime.activeInputTarget = null;
      }

      window.openSelection = function(button) {
        const wrapper = button && button.closest('.search-wrapper');
        const dialog = document.getElementById('medModal');
        const search = document.getElementById('dialogSuche');

        if (!wrapper || !dialog || !search) return;

        runtime.activeInputTarget = wrapper.querySelector('input[type="text"]');
        search.value = '';
        showResults(medicationValues().slice(0, 20));
        dialog.style.display = 'flex';
        search.focus();
      };

      document.addEventListener('click', function(event) {
        const plan = getMedicationPlan();
        const addButton = event.target.closest &&
          event.target.closest('[data-add-med-area]');

        if (plan && addButton && plan.contains(addButton)) {
          event.preventDefault();
          addDetailRow(addButton.getAttribute('data-add-med-area'));
          return;
        }

        if (
          event.target.id === 'closeDialogBtn' ||
          event.target.id === 'cancelDialogBtn'
        ) {
          event.preventDefault();
          closeModal();
          return;
        }

        const result = event.target.closest &&
          event.target.closest('#dialogListe li[data-wert]');

        if (result && runtime.activeInputTarget) {
          runtime.activeInputTarget.value = result.getAttribute('data-wert');
          runtime.activeInputTarget.dispatchEvent(
            new Event('input', { bubbles: true })
          );
          runtime.activeInputTarget.dispatchEvent(
            new Event('change', { bubbles: true })
          );
          closeModal();
          return;
        }

        if (event.target.id === 'medModal') closeModal();
      });

      function handleDetailChange(event) {
        const control = event.target;

        if (
          !control ||
          !control.matches ||
          !control.matches(
            '[data-medication-plan] [data-detail-field]'
          )
        ) {
          return;
        }

        const fieldName = String(
          control.getAttribute('data-detail-field') || ''
        ).trim().toUpperCase();

        if (fieldName === 'BEREICH') return;

        ensureRowArea(control.closest('tr'));
      }

      document.addEventListener('input', function(event) {
        if (event.target.id === 'dialogSuche') {
          const query = String(event.target.value || '').toLowerCase().trim();
          const values = medicationValues();

          showResults(
            query.length === 0
              ? values.slice(0, 20)
              : values.filter(value => value.toLowerCase().includes(query))
          );
          return;
        }

        handleDetailChange(event);
      });

      document.addEventListener('change', handleDetailChange);

      document.addEventListener('focusin', function(event) {
        const field = event.target;

        if (
          !field.matches ||
          !field.matches(
            '[data-medication-plan] input[data-detail-field="VDATUM"]'
          ) ||
          String(field.value || '').trim() !== ''
        ) {
          return;
        }

        const today = new Date();
        const day = String(today.getDate()).padStart(2, '0');
        const month = String(today.getMonth() + 1).padStart(2, '0');

        field.value = day + '.' + month + '.' + today.getFullYear();
        field.dispatchEvent(new Event('input', { bubbles: true }));
        field.dispatchEvent(new Event('change', { bubbles: true }));
      });

      runtime.observer = new MutationObserver(function(mutations) {
        const detailMarkupChanged = mutations.some(mutation =>
          mutation.type === 'childList' &&
          (
            Array.from(mutation.addedNodes).some(node =>
              node.nodeType === 1 &&
              (
                node.matches('[data-medication-plan], [data-med-area-list], tr') ||
                node.querySelector &&
                node.querySelector(
                  '[data-medication-plan], [data-med-area-list], tr'
                )
              )
            ) ||
            Array.from(mutation.removedNodes).some(node =>
              node.nodeType === 1 &&
              (
                node.matches('[data-medication-plan], [data-med-area-list], tr') ||
                node.querySelector &&
                node.querySelector(
                  '[data-medication-plan], [data-med-area-list], tr'
                )
              )
            )
          )
        );

        if (detailMarkupChanged) organizeMedicationRows();
      });

      runtime.observer.observe(document.body, {
        childList: true,
        subtree: true
      });

      organizeMedicationRows();
    })();
  </script>

</body>
</html>

Dieses Beispiel zeigt insbesondere:

Master-/Detail
Masterfelder
mehrere Detailcontainer
gemeinsames Detailtemplate
fachliche Aufteilung über BEREICH
MCDMS-Anzeigewerte
eigene JavaScript-Logik
responsive Darstellung

4. Leistungs- und Minutenerfassung

Ziel

Dieses Beispiel zeigt ein Master-/Detailformular, bei dem die Bedienung nicht nur aus normalen Eingabefeldern besteht.

Der Benutzer kann häufig benötigte Werte direkt über Schaltflächen auswählen.

Beispielsweise:

Leistungskategorie
5 Minuten
10 Minuten
15 Minuten
20 Minuten
30 Minuten
45 Minuten
60 Minuten

Zusätzlich enthält eine Zeile:

Datum
Handzeichen
Bemerkung

Datenmodell

Master:

FRM_LEISTUNGSERFASSUNG

Detail:

FRM_LEISTUNGSERFASSUNG_DETAIL

Konfiguration beispielsweise:

Database=FRM_LEISTUNGSERFASSUNG;FRMID;PatientID#FRM_LEISTUNGSERFASSUNG_DETAIL;ID;FRMID;PatientID

Detailfelder

Beispielsweise:

KATEGORIE
DAUER_LEISTUNG
LEISTUNGSDATUM
USERID
BEMERKUNG

Das aktuelle Beispiel verwendet unter anderem DAUER_LEISTUNG, Datum, Handzeichen und Bemerkung innerhalb einer Detailzeile. LeistungsMinutenerfassung_Maste…

Besonderheit

Die Schaltflächen für Kategorie und Dauer sind reine Bedienoberfläche.

Im Hintergrund werden normale Detailfelder gesetzt.

Das heißt:

Button „30 min“
↓
DAUER_LEISTUNG = 30

Der Benutzer erhält damit eine schnelle Bedienung, während die Daten weiterhin in normalen Datenbankspalten gespeichert werden.

Partial Refresh

Dieses Beispiel ist auch wichtig für die JavaScript-Laufzeit.

Nach dem Speichern wird nicht die komplette WebAkte neu geladen.

Das Formular muss deshalb seine sichtbaren Buttonzustände immer wieder aus den tatsächlichen Feldwerten ableiten können.

MAIN.HTML
Copy
<!DOCTYPE html>
<html lang="de">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Leistungs- & Minutenerfassung</title>

    <style>
        .mcdms-form-scope {
            font-family: "Segoe UI", Arial, sans-serif;
            margin: 0;
            padding: 20px;
            background: #f4f7fb;
            color: #24364b;
        }

        .mcdms-form-scope .form-container {
            max-width: 900px;
            margin: 0;
            background: #ffffff;
            border-radius: 12px;
            padding: 24px;
            box-sizing: border-box;
        }

        .mcdms-form-scope h2 {
            margin-top: 0;
            margin-bottom: 6px;
            color: #1f3552;
        }

        .mcdms-form-scope .subtitle {
            margin-bottom: 28px;
            color: #6b7c91;
        }

        .mcdms-form-scope .form-group {
            margin-bottom: 28px;
        }

        .mcdms-form-scope .group-title {
            display: block;
            margin-bottom: 12px;
            font-weight: 600;
            color: #2f435e;
        }

        .mcdms-form-scope input[type="text"],
        .mcdms-form-scope input[type="date"],
        .mcdms-form-scope textarea {
            width: 100%;
            box-sizing: border-box;
            padding: 11px 12px;
            border: 1px solid #cbd5e1;
            border-radius: 7px;
            font-size: 15px;
	    color: #1e293b;
            background: #ffffff;
        }

        .mcdms-form-scope textarea {
            min-height: 100px;
            resize: vertical;
        }

        .mcdms-form-scope input:focus,
        .mcdms-form-scope textarea:focus {
            outline: none;
            border-color: #3b82f6;
            box-shadow: 0 0 0 2px rgba(59,130,246,0.12);
        }

        .mcdms-form-scope .touch-grid {
            display: grid;
            gap: 10px;
        }

        .mcdms-form-scope .grid-2 {
            grid-template-columns: repeat(2, minmax(0, 1fr));
        }

        .mcdms-form-scope .grid-4 {
            grid-template-columns: repeat(4, minmax(0, 1fr));
        }

        /*
            Die echten Radio-Buttons bleiben Standard-HTML.
            Sie werden nur optisch ausgeblendet.
        */
        .mcdms-form-scope .touch-grid input[type="radio"] {
            position: absolute;
            opacity: 0;
            pointer-events: none;
        }

        .mcdms-form-scope .touch-grid label {
            display: flex;
            align-items: center;
            justify-content: center;
            min-height: 54px;
            padding: 10px 12px;
            box-sizing: border-box;

            background: #f4f7fb;
            border: 2px solid #cbd5e1;
            border-radius: 8px;

            color: #40546d;
            font-weight: 600;
            text-align: center;
            cursor: pointer;
            user-select: none;

            transition: background-color 0.15s ease,
                        border-color 0.15s ease,
                        color 0.15s ease;
        }

        .mcdms-form-scope .touch-grid label:hover {
            background: #eaf2ff;
            border-color: #93b7ed;
        }

        /*
            Standard-CSS:
            Wenn Radio checked ist, wird nur das folgende Label dargestellt.
        */
        .mcdms-form-scope .touch-grid input[type="radio"]:checked + label {
            background: #3b82f6;
            border-color: #2563eb;
            color: #ffffff;
        }

        .mcdms-form-scope .touch-grid input[type="radio"]:focus + label {
            outline: 2px solid #2563eb;
            outline-offset: 2px;
        }

        .mcdms-form-scope .touch-grid input[type="radio"]:disabled + label {
            opacity: 0.6;
            cursor: default;
        }

        .mcdms-form-scope .hint {
            margin-top: 8px;
            color: #77879a;
            font-size: 13px;
        }

        @media (max-width: 700px) {
            .mcdms-form-scope .grid-4 {
                grid-template-columns: repeat(2, minmax(0, 1fr));
            }
        }

        @media (max-width: 480px) {
            .mcdms-form-scope .grid-2,
            .mcdms-form-scope .grid-4 {
                grid-template-columns: 1fr;
            }

            .mcdms-form-scope {
                padding: 10px;
            }

            .mcdms-form-scope .form-container {
                padding: 16px;
            }
        }
    </style>
</head>

<body>
<input type="hidden"
       name="ErstelltVon"
       data-field="ErstelltVon"
       value="[#ErstelltVon#]">

<input type="hidden"
       name="ErstelltAm"
       data-field="ErstelltAm"
       value="[#ErstelltAm#]">

<input type="hidden"
       name="DATE"
       data-field="DATE"
       value="[#DATE#]">

<div class="form-container">

    <h2>Leistungs- & Minutenerfassung</h2>
    <div class="subtitle">
        Pflegeleistung dokumentieren
    </div>

    <!-- DATUM -->
    <div class="form-group">
        <label class="group-title" for="DATUM">
            Datum der Leistung
        </label>

        <input
            type="text"
            id="DATUM"
            name="DATUM"
            data-field="DATUM"
            value="[#DATE#]"
            required>
    </div>

    <!-- PFLEGEBEREICH -->
    <div class="form-group">

        <span class="group-title">
            Pflegebereich / KTL-Kategorie
        </span>

        <div class="touch-grid grid-2">

            <input
                type="radio"
                id="pflege_aktiv"
                name="PFLEGEBEREICH"
                data-field="PFLEGEBEREICH"
                value="AKTIV"
                required>
            <label for="pflege_aktiv">
                🛏 Aktivierende Pflege
            </label>

            <input
                type="radio"
                id="pflege_schulung"
                name="PFLEGEBEREICH"
                data-field="PFLEGEBEREICH"
                value="SCHULUNG">
            <label for="pflege_schulung">
                💬 Anleitung / Schulung
            </label>

            <input
                type="radio"
                id="pflege_mobil"
                name="PFLEGEBEREICH"
                data-field="PFLEGEBEREICH"
                value="MOBIL">
            <label for="pflege_mobil">
                🚶 Mobilisation / Transfer
            </label>

            <input
                type="radio"
                id="pflege_ops"
                name="PFLEGEBEREICH"
                data-field="PFLEGEBEREICH"
                value="OPS">
            <label for="pflege_ops">
                🩺 OPS-Intervention
            </label>

            <input
                type="radio"
                id="pflege_wunde"
                name="PFLEGEBEREICH"
                data-field="PFLEGEBEREICH"
                value="WUNDE">
            <label for="pflege_wunde">
                🩹 Wundversorgung
            </label>

            <input
                type="radio"
                id="pflege_ernaehrung"
                name="PFLEGEBEREICH"
                data-field="PFLEGEBEREICH"
                value="ERN">
            <label for="pflege_ernaehrung">
                🍎 Ernährung / Hilfe
            </label>

        </div>
    </div>

    <!-- DAUER -->
    <div class="form-group">

        <span class="group-title">
            Dauer der Maßnahme
        </span>

        <div class="touch-grid grid-4">

            <input
                type="radio"
                id="dauer_5"
                name="DAUER_LEISTUNG"
                data-field="DAUER_LEISTUNG"
                value="5"
                required>
            <label for="dauer_5">5 min</label>

            <input
                type="radio"
                id="dauer_10"
                name="DAUER_LEISTUNG"
                data-field="DAUER_LEISTUNG"
                value="10">
            <label for="dauer_10">10 min</label>

            <input
                type="radio"
                id="dauer_15"
                name="DAUER_LEISTUNG"
                data-field="DAUER_LEISTUNG"
                value="15">
            <label for="dauer_15">15 min</label>

            <input
                type="radio"
                id="dauer_20"
                name="DAUER_LEISTUNG"
                data-field="DAUER_LEISTUNG"
                value="20">
            <label for="dauer_20">20 min</label>

            <input
                type="radio"
                id="dauer_25"
                name="DAUER_LEISTUNG"
                data-field="DAUER_LEISTUNG"
                value="25">
            <label for="dauer_25">25 min</label>

            <input
                type="radio"
                id="dauer_30"
                name="DAUER_LEISTUNG"
                data-field="DAUER_LEISTUNG"
                value="30">
            <label for="dauer_30">30 min</label>

            <input
                type="radio"
                id="dauer_45"
                name="DAUER_LEISTUNG"
                data-field="DAUER_LEISTUNG"
                value="45">
            <label for="dauer_45">45 min</label>

            <input
                type="radio"
                id="dauer_60"
                name="DAUER_LEISTUNG"
                data-field="DAUER_LEISTUNG"
                value="60">
            <label for="dauer_60">60 min</label>

        </div>

        <div class="hint">
            Reine Netto-Therapiezeit ohne Wegezeiten.
        </div>
    </div>

    <!-- HANDZEICHEN -->
    <div class="form-group">

        <label class="group-title" for="HANDZEICHEN">
            Handzeichen
        </label>

        <input
            type="text"
            id="HANDZEICHEN"
            name="HANDZEICHEN"
            data-field="HANDZEICHEN"
            value="[#USERNAME#]"
            maxlength="10">
    </div>

    <!-- BEMERKUNG -->
    <div class="form-group">

        <label class="group-title" for="BEMERKUNG">
            Bemerkung
        </label>	 
        <textarea
            id="BEMERKUNG"
            name="BEMERKUNG"
            data-field="BEMERKUNG">[#BEMERKUNG#]</textarea>

    </div>
</div>
<script>
(function () {

    const field =
        document.querySelector(
            'input[name="DATE"][data-field="DATE"]'
        );

    if (!field) {
        return;
    }

    const today = new Date();

    const year = today.getFullYear();
    const month = String(today.getMonth() + 1).padStart(2, '0');
    const day = String(today.getDate()).padStart(2, '0');

    field.value =
        year + '-' + month + '-' + day;

    field.dispatchEvent(
        new Event('input', { bubbles: true })
    );

    field.dispatchEvent(
        new Event('change', { bubbles: true })
    );

})();
</script>
</body>
</html>

MAIN.HTML
Copy
<!DOCTYPE html>
<html lang="de">
<head>
    <meta charset="UTF-8">
    <title>Leistungs- & Minutenerfassung (Multierfassung)</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 0; padding: 12px; color: #222; background: transparent; }
        .header-title { font-size: 16px; font-weight: bold; margin-bottom: 4px; }
        .main-table { width: 100%; border-collapse: collapse; background: transparent; }
        .main-table td { padding: 0; border: 0; vertical-align: top; }
        .main-table tr + tr td { padding-top: 12px; }
        .detail-block { position: relative; padding: 0 42px 12px 0; margin-bottom: 10px; }
        .section-title { font-size: 14px; font-weight: bold; margin: 0 0 6px 0; color: #334155; }
        .btn-row { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin-bottom: 8px; }
        .opt-btn { background: #fff; color: #334155; border: 1px solid #c8c8c8; border-radius: 4px; padding: 8px 12px; min-height: 36px; font-size: 14px; cursor: pointer; text-align: left; white-space: nowrap; }
        .opt-btn:hover { background: #f5f5f5; border-color: #999; }
        .opt-btn.active { background: #00569d; color: #fff; border-color: #004075; font-weight: bold; }
        .fields-grid { display: grid; grid-template-columns: 220px 240px minmax(260px, 400px); gap: 0; margin-top: 6px; max-width: 880px; }
        .field-head { background: #eeeeee; color: #334155; padding: 7px 10px; font-weight: bold; font-size: 14px; border-right: 3px solid #fff; }
        .field-cell { padding: 6px 8px 0 8px; }
        .field-cell input[type="text"] { width: 100%; box-sizing: border-box; padding: 9px 10px; border: 1px solid #c8c8c8; border-radius: 4px; font-size: 14px; background: #fff; color: #334155; }
        .field-cell input[readonly] { background: #f7f7f7; color: #64748b; }
	.field-cell input[data-detail-field="LEISTUNGSDATUM"] {
 	   background: #00569d;
	    color: #ffffff;
	    border-color: #004075;
	    font-weight: normal;
	}
	.field-cell input[data-detail-field="LEISTUNGSDATUM"]:focus {
	    outline: none;
	    box-shadow: 0 0 0 3px rgba(0,86,157,.15);
	}
	
        .btn-delete { position: absolute; top: 0; right: 0; background: #dc3545; color: #fff; border: 0; border-radius: 3px; width: 22px; height: 22px; padding: 0; cursor: pointer; font-size: 14px; line-height: 22px; }
        .btn-delete:hover { background: #bd2130; }
        .btn-add-row { background: #10b981; color: white; border: none; padding: 9px 18px; font-size: 14px; border-radius: 4px; cursor: pointer; margin-top: 4px; }
        .btn-add-row:hover { background: #059669; }
        .footer-text {
 	   font-size: 13px;
	    color: #777;
	    margin-top: 0;
	    text-align: right;
	}
	.bottom-actions {
 	  display: flex;
	  flex-direction: column;
	  align-items: flex-end;
	  gap: 8px;
	}
/* 
   Rahmen für den Detailbereich (Card-Layout)
   Dieses CSS ist isoliert und beeinträchtigt das restliche Seitenlayout nicht.
*/

.detail-card-container {
    background-color: #ffffff;
    border: 1px solid #e2e8f0;    /* Subtiler, grauer Rahmen */
    border-radius: 12px;         /* Abgerundete Ecken passend zum Beispiel */
    box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.05), 0 1px 2px 0 rgba(0, 0, 0, 0.03); /* Leichter Schatten */
    padding: 20px;               /* Innenabstand für die Inhalte */
    max-width: 100%;             /* Verhindert das Ausbrechen aus dem Layout */
    box-sizing: border-box;
}

/* Optional: Falls du die Trennlinien zwischen den Zeilen wie im Beispiel möchtest */
.detail-card-container table, 
.detail-card-container .detail-rows {
    width: 100%;
    border-collapse: collapse;
}

.detail-card-container tr, 
.detail-card-container .detail-row {
    border-bottom: 1px solid #edf2f7; /* Trennlinie zwischen den Daten */
}

.detail-card-container tr:last-child, 
.detail-card-container .detail-row:last-child {
    border-bottom: none; /* Keine Linie nach der letzten Zeile */
}
        @media (max-width: 850px) {
            .fields-grid { grid-template-columns: 1fr; }
            .field-head { margin-top: 6px; border-right: 0; }
            .field-cell { padding: 5px 0 0 0; }
            .detail-block { padding-right: 34px; }
        }
    </style>
</head>
<body>
    <div class="header-title">Leistungs- & Minutenerfassung (Multierfassung)</div>

    <div id="detailDefaults" style="display:none;">
        <span data-default="LEISTUNGSDATUM">[#DATE#]</span>
        <span data-default="USERID">[#USERID#]</span>
    </div>

    <table class="main-table">
        <tbody data-mcdms-detail-list></tbody>
    </table>
    <template data-mcdms-detail-row>
        <tr>
            <td>
		<div class="detail-card-container">

                <div class="detail-block">
                    <button type="button" class="btn-delete" data-delete-row title="Zeile entfernen">✕</button>

                    <div class="section-title">Pflegebereich / KTL-Kategorie</div>
                    <input type="hidden" data-detail-field="KATEGORIE" value="">
                    <div class="btn-row">
                        <button type="button" class="opt-btn" data-category="AKTIV">🛏 Aktivierende Pflege</button>
                        <button type="button" class="opt-btn" data-category="SCHULUNG">💬 Anleitung / Schulung</button>
                        <button type="button" class="opt-btn" data-category="MOBIL">🚶 Mobilisation / Transfer</button>
                        <button type="button" class="opt-btn" data-category="OPS">🩺 OPS-Intervention</button>
                        <button type="button" class="opt-btn" data-category="WUNDE">🩹 Wundversorgung</button>
                        <button type="button" class="opt-btn" data-category="ERN">🍎 Ernährung / Hilfe</button>
                    </div>

                    <div class="section-title">Dauer der Maßnahme (Minuten)</div>
                    <input type="hidden" data-detail-field="DAUER_LEISTUNG" value="">
                    <div class="btn-row">
                        <button type="button" class="opt-btn" data-duration="5">5 min</button>
                        <button type="button" class="opt-btn" data-duration="10">10 min</button>
                        <button type="button" class="opt-btn" data-duration="15">15 min</button>
                        <button type="button" class="opt-btn" data-duration="20">20 min</button>
                        <button type="button" class="opt-btn" data-duration="25">25 min</button>
                        <button type="button" class="opt-btn" data-duration="30">30 min</button>
                        <button type="button" class="opt-btn" data-duration="45">45 min</button>
                        <button type="button" class="opt-btn" data-duration="60">60 min</button>
                    </div>

                    <div class="fields-grid">
                        <div class="field-head">Datum</div>
                        <div class="field-head">Handzeichen</div>
                        <div class="field-head">Bemerkung</div>

                        <div class="field-cell">
                            <input type="text" data-detail-field="LEISTUNGSDATUM" value="" maxlength="10">
                        </div>
                        <div class="field-cell">
                            <input type="text" data-detail-field="USERID" value="" readonly>
                        </div>
                        <div class="field-cell">
                            <input type="text" data-detail-field="BEMERKUNG" placeholder="Bemerkung eingeben...">
                        </div>
                    </div>
                </div>
		</div>
            </td>
        </tr>
    </template>

    <div class="bottom-actions">
    <button type="button" class="btn-add-row" id="addRowButton">
        + Zeile hinzufügen
    </button>

    <div class="footer-text">
        Reine Netto-Therapiezeit ohne Wegezeiten.
    </div>
</div>

    <script>
        (function () {
            const tbody = document.querySelector('[data-mcdms-detail-list]');
            const template = document.querySelector('template[data-mcdms-detail-row]');
            const addButton = document.getElementById('addRowButton');

            function setActiveButton(row, selector, value) {
                row.querySelectorAll(selector).forEach(function (button) {
                    const buttonValue = button.getAttribute('data-category') || button.getAttribute('data-duration') || '';
                    button.classList.toggle('active', String(buttonValue) === String(value || ''));
                });
            }

            function initializeRow(row) {
                if (!row || row.dataset.initialized === '1') return;
                row.dataset.initialized = '1';

                const categoryField = row.querySelector('[data-detail-field="KATEGORIE"]');
                const durationField = row.querySelector('[data-detail-field="DAUER_LEISTUNG"]');

                row.querySelectorAll('[data-category]').forEach(function (button) {
                    button.addEventListener('click', function () {
                        if (!categoryField) return;
                        categoryField.value = button.getAttribute('data-category') || '';
                        setActiveButton(row, '[data-category]', categoryField.value);
                        categoryField.dispatchEvent(new Event('input', { bubbles: true }));
                        categoryField.dispatchEvent(new Event('change', { bubbles: true }));
                    });
                });

                row.querySelectorAll('[data-duration]').forEach(function (button) {
                    button.addEventListener('click', function () {
                        if (!durationField) return;
                        durationField.value = button.getAttribute('data-duration') || '';
                        setActiveButton(row, '[data-duration]', durationField.value);
                        durationField.dispatchEvent(new Event('input', { bubbles: true }));
                        durationField.dispatchEvent(new Event('change', { bubbles: true }));
                    });
                });

                const deleteButton = row.querySelector('[data-delete-row]');
                if (deleteButton) {
                    deleteButton.addEventListener('click', function () {
                        const kategorie =
                            row.querySelector('[data-detail-field="KATEGORIE"]')?.value || "";

                        const dauer =
                            row.querySelector('[data-detail-field="DAUER_LEISTUNG"]')?.value || "";

                        const bemerkung =
                            row.querySelector('[data-detail-field="BEMERKUNG"]')?.value || "";

                        const istBefuellt =
                            kategorie.trim() !== "" ||
                            dauer.trim() !== "" ||
                            bemerkung.trim() !== "";

                        if (!istBefuellt) {
                            row.remove();
                            return;
                        }

                        if (confirm('Möchten Sie diese Zeile wirklich löschen?')) {
                            row.remove();
                        }
                    });
                }

                if (categoryField) setActiveButton(row, '[data-category]', categoryField.value);
                if (durationField) setActiveButton(row, '[data-duration]', durationField.value);
            }

            function addNewRow() {
                const fragment = template.content.cloneNode(true);
                const row = fragment.querySelector('tr');

                const currentDate = document.querySelector('[data-default="LEISTUNGSDATUM"]')?.textContent.trim() || '';
                const currentUserId = document.querySelector('[data-default="USERID"]')?.textContent.trim() || '';

                const dateField = row.querySelector('[data-detail-field="LEISTUNGSDATUM"]');
                const userField = row.querySelector('[data-detail-field="USERID"]');

                if (dateField) dateField.value = currentDate;
                if (userField) userField.value = currentUserId;

                tbody.appendChild(fragment);
                initializeRow(row);
            }

            tbody.querySelectorAll('tr').forEach(initializeRow);
            if (addButton) addButton.addEventListener('click', addNewRow);
            if (!tbody.querySelector('tr')) addNewRow();

            function synchronizeRowSelection(row) {
                if (!row) return;

                const categoryField = row.querySelector('[data-detail-field="KATEGORIE"]');
                const durationField = row.querySelector('[data-detail-field="DAUER_LEISTUNG"]');

                if (categoryField) setActiveButton(row, '[data-category]', categoryField.value);
                if (durationField) setActiveButton(row, '[data-duration]', durationField.value);
            }

            const observer = new MutationObserver(function (mutations) {
                const rowsToSynchronize = new Set();

                mutations.forEach(function (mutation) {
                    if (mutation.type === 'childList') {
                        mutation.addedNodes.forEach(function (node) {
                            if (node.nodeType !== 1) return;

                            if (node.matches && node.matches('tr')) {
                                initializeRow(node);
                                rowsToSynchronize.add(node);
                            }

                            if (node.querySelectorAll) {
                                node.querySelectorAll('tr').forEach(function (row) {
                                    initializeRow(row);
                                    rowsToSynchronize.add(row);
                                });
                            }
                        });
                    }

                    if (mutation.type === 'attributes') {
                        const row = mutation.target.closest && mutation.target.closest('tr');
                        if (row) rowsToSynchronize.add(row);
                    }
                });

                rowsToSynchronize.forEach(synchronizeRowSelection);
            });

            observer.observe(tbody, {
                childList: true,
                subtree: true,
                attributes: true,
                attributeFilter: ['class']
            });
        })();
    </script>
</body>
</html>

5. Access-Formular

Ziel

Nicht jedes Formular benötigt SQL Server.

Für vorhandene MCDMS-Datenbestände kann die WebAkte auch direkt mit einer Access-Datenbank arbeiten.

Der Aufbau des HTML-Formulars unterscheidet sich dabei grundsätzlich nicht von einem SQL-Formular. Der Unterschied liegt in der Konfiguration der Datenquelle über DatabaseFile.

Dieses Beispiel zeigt:

Access statt SQL Server
MultiRecord
Neu / Speichern / Löschen / Duplizieren
normale Masterfelder
feste Access-Datei
optional dynamische Access-Datei über {IDDEV}

Datenbank

Beispieldatei:

{APPPATH}\DATABASE\FORMULAR\ERGOMETERTRAINING.MDB

Darin befindet sich beispielsweise die Tabelle:

FORMULAR

mit Feldern wie:

FRMID
PatientID
DATUM
DAUER
BELASTUNG
PULS
BEMERKUNG

Dabei ist beispielsweise:

FRMID

der technische Primärschlüssel und:

PatientID

die Zuordnung zum aktuellen Patienten.

Diese technischen Werte werden nicht vom Browser bestimmt.

FORMULARE.INI

[FORMULAR_ERGOMETER]
Title=Ergometertraining
TitleRow=[#DATUM#] | [#DAUER#] Min.

HTML=HTML\Therapie\ERGOMETERTRAINING.html

Edit=TRUE
MultiRecord=TRUE
Delete=TRUE
AllowDuplicate=TRUE
DoubleSaveButton=TRUE

DatabaseFile=DATABASE\FORMULAR\ERGOMETERTRAINING.MDB
Database=FORMULAR;FRMID;PatientID

OrderBy=FRMID;DESC

Die wichtige Besonderheit gegenüber SQL Server ist:

DatabaseFile=DATABASE\FORMULAR\ERGOMETERTRAINING.MDB

Damit weiß die WebAkte, dass die Formulardaten aus dieser Access-Datei geladen und dort gespeichert werden.

Die eigentliche Tabellenbeschreibung bleibt:

Database=FORMULAR;FRMID;PatientID

also:

Tabelle
Primärschlüssel
Patientenfeld

HTML

Das HTML verwendet dieselben normalen Masterfelder wie ein SQL-Formular.

Beispielsweise:

<input type="date"
       name="DATUM"
       data-field="DATUM">

oder:

<input type="number"
       name="DAUER"
       data-field="DAUER">

oder:

<textarea name="BEMERKUNG"
          data-field="BEMERKUNG"></textarea>

Das Formular muss also nicht wissen, ob die Daten später in SQL Server oder Access gespeichert werden.

Die Datenquelle wird ausschließlich durch FORMULARE.INI bestimmt.

Beispielinhalt

Das Ergometerformular könnte beispielsweise folgende Felder enthalten:

Datum
Dauer in Minuten
Belastung
Puls
Bemerkung

Ein Patient kann durch:

MultiRecord=TRUE

mehrere Trainingseinträge besitzen.

Beispielsweise:

12.10.2026 | 20 Min.
13.10.2026 | 25 Min.
14.10.2026 | 30 Min.

Durch:

OrderBy=FRMID;DESC

erscheinen die zuletzt angelegten Einträge zuerst.

Ablauf

Beim Öffnen des Formulars lädt die WebAkte die zum aktuellen Patienten gehörenden Datensätze aus:

ERGOMETERTRAINING.MDB

und dort aus der Tabelle:

FORMULAR

Bei + Neu wird zunächst ein neuer Formularzustand geöffnet.

Beim Speichern wird ein neuer Datensatz angelegt und automatisch dem aktuellen Patienten zugeordnet.

Vorhandene Datensätze können abhängig von der Konfiguration:

bearbeitet
gelöscht
dupliziert

werden.

Dynamische Access-Datei

Eine besondere Variante sind MCDMS-Datenbestände, bei denen die Access-Datei vom Patientenblock abhängt.

Dann kann beispielsweise verwendet werden:

DatabaseFile=DATABASE\MEDDOK{IDDEV}.MDB

Die restliche Tabellenkonfiguration bleibt beispielsweise:

Database=FORMULAR;FRMID;PatientID

{IDDEV} wird serverseitig aus dem aktuellen MCDMS-Patientenkontext bestimmt.

Beispielsweise kann daraus:

DATABASE\MEDDOK0.MDB

oder:

DATABASE\MEDDOK1.MDB

werden.

Welcher Wert verwendet wird, bestimmt die WebAkte aus dem gesicherten Patientenkontext.

Der Browser liefert {IDDEV} nicht selbst.

Wichtig

Auch bei einer dynamischen Access-Datei bleibt das HTML unverändert.

Dasselbe Formular kann beispielsweise mit:

DatabaseFile=DATABASE\FORMULAR\ERGOMETERTRAINING.MDB

oder:

DatabaseFile=DATABASE\MEDDOK{IDDEV}.MDB

arbeiten.

Das ist gerade bei bestehenden MCDMS-Datenstrukturen hilfreich, weil vorhandene Access-Datenbanken direkt eingebunden werden können.

HTML

Accordion: Vollständiges HTML – Access-Formular

MAIN.HTML
Copy
<!doctype html>
<html lang="de">

<head>
    <meta charset="utf-8">

    <meta name="viewport"
          content="width=device-width, initial-scale=1.0">

    <title>Ergometertraining</title>

    <style>
        body {
            margin: 0;
            background: transparent;
        }

        .ergometer-form,
        .ergometer-form * {
            box-sizing: border-box;
        }

        .ergometer-form {
            max-width: 900px;
            margin: 0 auto;
            padding: 20px;
            font-family: "Segoe UI", Arial, sans-serif;
            color: #1f2937;
        }

        .ergometer-card {
            padding: 20px;
            background: #ffffff;
            border: 1px solid #d8e0e8;
            border-radius: 10px;
        }

        .ergometer-grid {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 16px;
            margin-bottom: 16px;
        }

        .ergometer-form label {
            display: grid;
            gap: 6px;
            font-size: 14px;
            font-weight: 600;
        }

        .ergometer-form input,
        .ergometer-form textarea {
            width: 100%;
            padding: 8px 10px;
            border: 1px solid #cbd5e1;
            border-radius: 6px;
            background: #ffffff;
            color: #1f2937;
            font: inherit;
            font-weight: 400;
        }

        .ergometer-form textarea {
            min-height: 120px;
            resize: vertical;
        }

        @media (max-width: 700px) {
            .ergometer-grid {
                grid-template-columns: 1fr;
            }
        }
    </style>
</head>

<body>

<form class="ergometer-form">

    <div class="ergometer-card">

        <h2>Ergometertraining</h2>

        <p>
            Patient:
            <strong>[#GESAMTNAME#]</strong>
        </p>

        <div class="ergometer-grid">

            <label>
                Datum

                <input type="date"
                       name="DATUM"
                       data-field="DATUM">
            </label>

            <label>
                Dauer in Minuten

                <input type="number"
                       name="DAUER"
                       data-field="DAUER"
                       min="0">
            </label>

            <label>
                Belastung in Watt

                <input type="number"
                       name="BELASTUNG"
                       data-field="BELASTUNG"
                       min="0">
            </label>

            <label>
                Puls

                <input type="number"
                       name="PULS"
                       data-field="PULS"
                       min="0">
            </label>

        </div>

        <label>
            Bemerkung

            <textarea name="BEMERKUNG"
                      data-field="BEMERKUNG"></textarea>
        </label>

    </div>

</form>

</body>
</html>

Hier kommt das vollständige Beispiel ERGOMETERTRAINING.html hinein.

Das Beispiel sollte mindestens enthalten:

DATUM
DAUER
BELASTUNG
PULS
BEMERKUNG

und damit zeigen, dass sich ein Access-Formular im HTML genauso verhält wie ein normales SQL-Masterformular.

Was zeigt dieses Beispiel?

Dieses Beispiel zeigt gemeinsam:

Access als Datenquelle
DatabaseFile
normale Database-Konfiguration
MultiRecord
TitleRow
Neu
Speichern
Löschen
Duplizieren
normale HTML-Masterfelder
feste MDB
dynamische MDB über {IDDEV}

Der wesentliche Unterschied zu SQL Server liegt damit nicht im HTML, sondern in der Auswahl der Datenquelle über:

DatabaseFile=...

6. MCDMS-Scripts und Actions

Ziel

Dieses Beispiel zeigt ein Formular, das neben Datenbankfeldern auch vorhandene MCDMS-Scriptlogik verwendet.

Ein Formular kann unterschiedliche Scriptphasen besitzen:

ScriptLoadHTML
ScriptSaveHTML
ScriptDelete

Zusätzlich können frei definierte Actions verwendet werden.

FORMULARE.INI

Beispiel:

[FORMULAR_SCRIPTBEISPIEL]
Title=Scriptbeispiel

HTML=HTML\Beispiele\SCRIPTBEISPIEL.html

Edit=TRUE
MultiRecord=TRUE
Delete=TRUE

Database=FRM_SCRIPTBEISPIEL;FRMID;PatientID

ScriptLoadHTML=CONFIG\Scripts\WEB\load.mcfscr
ScriptSaveHTML=CONFIG\Scripts\WEB\save.mcfscr
ScriptDelete=CONFIG\Scripts\WEB\delete.mcfscr

Action_DRUCKEN=CONFIG\Scripts\WEB\drucken.mcfscr
ActionReload_DRUCKEN=TRUE

ScriptLoadHTML

Wird vor dem Rendern ausgeführt.

Damit können beispielsweise PatientVars vorbereitet werden, die anschließend im HTML verwendet werden:

<div>
    [#TESTVAR#]
</div>

ScriptSaveHTML

Läuft nach erfolgreichem Datenbank-Commit.

Die Daten sind zu diesem Zeitpunkt bereits gespeichert.

ScriptDelete

Läuft vor dem eigentlichen Löschen.

Nur bei erfolgreichem Scriptablauf wird der Datensatz anschließend gelöscht.

Action

HTML:

<button type="button"
        data-mcdms-action="DRUCKEN">
    Drucken
</button>

Der Browser kennt nur den Action-Namen:

DRUCKEN

Der tatsächliche Scriptpfad stammt aus FORMULARE.INI.

HTML

MAIN.HTML
Copy
<!doctype html>
<html lang="de">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Webtest Aktionen</title>
  
  <!-- Icon-Schriftart für ein modernes UI -->
  <link rel="stylesheet" href="https://cloudflare.com">

  <style>
    /* --- FALLBACKS FÜR DEINE MCDMS VARIABLEN (falls das System sie nicht bereitstellt) --- */
    .mcdms-form-scope {
      --primary: #2b6cb0;
      --primary-hover: #2c5282;
      --action: #2b6cb0;
      --secondary: #718096;
      --text: #2d3748;
      --background: #f4f6f9;
      --surface: #ffffff;
      --divider: #e2e8f0;
      --radius: 8px;
      --white: #ffffff;
      --accent: #3182ce;

      color-scheme: light;
      color: var(--text);
    }

    /* --- DEIN BESTEHENDER MCMD-CODE --- */
    .mcdms-form-scope .webtest-action-page {
  display: grid;
  gap: 18px;
  width: 100%;
  font-family: 'Segoe UI', system-ui, sans-serif;
  background: var(--surface);
  color: var(--text);
  padding: 20px;
  box-sizing: border-box;
}
    .mcdms-form-scope .webtest-action-header {
      display: grid;
      gap: 4px;
    }
    .mcdms-form-scope .webtest-action-title {
      margin: 0;
      font-size: 1.4rem;
      color: var(--text);
    }
    .mcdms-form-scope .webtest-action-hint {
      margin: 0;
      color: var(--secondary);
      line-height: 1.45;
    }
    .mcdms-form-scope .webtest-action-controls {
      display: flex;
      flex-wrap: wrap;
      align-items: center;
      gap: 10px;
    }
    .mcdms-form-scope .action-button {
      display: inline-flex;
      min-height: 48px;
      align-items: center;
      justify-content: center;
      padding: 0 18px;
      border: 1px solid var(--action);
      border-radius: var(--radius);
      background: var(--action);
      color: var(--white);
      font: inherit;
      font-weight: 600;
      cursor: pointer;
      transition: background 0.2s, border-color 0.2s;
    }
    .mcdms-form-scope .action-button:hover {
      border-color: var(--primary-hover);
      background: var(--primary-hover);
    }

    .mcdms-form-scope .action-button-secondary {
      border-color: #2d3748;
      background: #2d3748;
      color: #ffffff;
    }

    .mcdms-form-scope .action-button-secondary:hover {
      border-color: #1a202c;
      background: #1a202c;
      color: #ffffff;
    }
    .mcdms-form-scope .action-button:disabled {
      opacity: .65;
      cursor: default;
    }
    .mcdms-form-scope .internal-link {
      display: inline-flex;
      min-height: 48px;
      align-items: center;
      justify-content: center;
      padding: 0 18px;
      border: 1px solid var(--divider);
      border-radius: var(--radius);
      background: var(--surface);
      color: var(--text);
      text-decoration: none;
      font-weight: 500;
      transition: all 0.2s;
    }
    .mcdms-form-scope .internal-link:hover {
      border-color: var(--primary);
      background: var(--background);
    }
    .mcdms-form-scope .internal-link:focus-visible {
      outline: 3px solid var(--accent);
      outline-offset: 2px;
    }
    .mcdms-form-scope .action-result {
      display: grid;
      gap: 8px;
      min-height: 72px;
      padding: 14px;
      border: 1px solid var(--divider);
      border-radius: var(--radius);
      background: var(--surface);
      color: var(--text);
      box-shadow: 0 4px 6px rgba(0,0,0,0.02);
    }
    .mcdms-form-scope .action-result-label {
      color: var(--secondary);
      font-size: .9rem;
    }
    .mcdms-form-scope .action-result-content {
      min-width: 0;
      line-height: 1.5;
      overflow-wrap: anywhere;
      color: var(--text);
    }

    /* --- MODERNES POPUP / MODAL DESIGN --- */
    .mcdms-form-scope .modal-overlay {
      position: fixed;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      background-color: rgba(0, 0, 0, 0.4);
      backdrop-filter: blur(3px);
      display: flex;
      align-items: center;
      justify-content: center;
      z-index: 1000;
      opacity: 0;
      pointer-events: none;
      transition: opacity 0.2s ease;
    }
    .mcdms-form-scope .modal-overlay.modal-active {
      opacity: 1;
      pointer-events: auto;
    }
    .mcdms-form-scope .modal-box {
      background: var(--surface);
      color: var(--text);
      width: 90%;
      max-width: 500px;
      border-radius: 12px;
      box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
      display: flex;
      flex-direction: column;
      overflow: hidden;
      transform: translateY(-20px);
      transition: transform 0.2s ease;
    }
    .mcdms-form-scope .modal-overlay.modal-active .modal-box {
      transform: translateY(0);
    }
    .mcdms-form-scope .modal-header {
      background: var(--background);
      padding: 16px 20px;
      border-bottom: 1px solid var(--divider);
      display: flex;
      justify-content: space-between;
      align-items: center;
    }
    .mcdms-form-scope .modal-header h3 {
      color: var(--primary);
      margin: 0;
      font-size: 1.15rem;
      display: flex;
      align-items: center;
      gap: 8px;
    }
    .mcdms-form-scope .modal-close-x {
      background: none;
      border: none;
      font-size: 1.5rem;
      color: var(--secondary);
      cursor: pointer;
      line-height: 1;
    }
    .mcdms-form-scope .modal-body {
      padding: 20px;
      font-size: 0.95rem;
      line-height: 1.5;
      color: var(--text);
    }
    .mcdms-form-scope .modal-footer-buttons {
      padding: 16px 20px;
      border-top: 1px solid var(--divider);
      display: flex;
      justify-content: flex-end;
      gap: 12px;
      background: var(--background);
    }
  </style>
</head>
<body style="background: #ffffff; color: #2d3748; padding: 20px;">

  <!-- Wrapper hinzugefügt, damit das CSS deines Systems (.mcdms-form-scope) greift -->
  <div class="mcdms-form-scope">
    
    <div class="webtest-action-page">
      <header class="webtest-action-header">
	<img src="/IMAGES/aspi.PNG" alt="Kliniklogo" style="height: 80px; width: auto;">
        <h1 class="webtest-action-title">Webtest Aktionen</h1>
        <p class="webtest-action-hint">Testseite für serverseitig konfigurierte MCDMS-Aktionen.</p>
      </header>
      
      <div class="webtest-action-controls">
        <button type="button" class="action-button" data-mcdms-action="TEST">Testaktion ausführen</button>
        <a class="internal-link" href="/patients/[#PATIENTID#]/areas/medikation">
          <i class="fa-solid fa-capsules" style="margin-right: 6px;"></i> Gehe zu Medikation
        </a>
        
        <!-- POPUP BUTTON -->
        <button type="button" onclick="openModal()" class="action-button action-button-secondary">
          <i class="fa-solid fa-comment-dots" style="margin-right: 6px;"></i> Nachricht anzeigen
        </button>
      </div>
      
      <div class="action-result">
        <strong class="action-result-label">TESTVAR:</strong>
        <div class="action-result-content" data-mcdms-html="TESTVAR"></div>

      </div>
	<h1 class="webtest-action-title">Beispiel für Klinik-Logo: Systemweit verfügbar!</h1>

	<img src="/IMAGES/LOGO.PNG" alt="Kliniklogo" style="height: 80px; width: auto;">

    </div>
    <!-- POPUP-FENSTER (MODAL) -->
    <div id="customModal" class="modal-overlay" onclick="closeModalOnOverlay(event)">
        <div class="modal-box">
            <div class="modal-header">
                <h3><i class="fa-solid fa-circle-info"></i> System-Meldung</h3>
                <button type="button" class="modal-close-x" onclick="closeModal()">×</button>
            </div>
            <div class="modal-body">
                <p>Das ist dein neues, modernes Popup-Fenster!</p>
                <p style="color: var(--secondary); font-size: 0.85rem; margin-top: 8px;">Es passt sich nahtlos an dein Systemdesign an.</p>
            </div>
            <div class="modal-footer-buttons">
                <!-- Nutzt deine Systemklassen für konsistente Button-Styles -->
                <button type="button" class="internal-link" style="min-height: 38px;" onclick="closeModal()">Schließen</button>
                <button type="button" class="action-button" style="min-height: 38px;" onclick="closeModal()">OK</button>
            </div>
        </div>
    </div>
  </div>

  <!-- JAVASCRIPT LOGIK -->
  <script>
    function openModal() {
        document.getElementById('customModal').classList.add('modal-active');
    }

    function closeModal() {
        document.getElementById('customModal').classList.remove('modal-active');
    }

    function closeModalOnOverlay(event) {
        if (event.target.classList.contains('modal-overlay')) {
            closeModal();
        }
    }
  </script>

</body>
</html>

7. Patientenübersicht ohne eigene Datenbank

Ziel

Nicht jede Seite benötigt eine eigene Formular-Datenbank.

Ein datenbankloses Formular kann beispielsweise als Patientenübersicht oder Dashboard verwendet werden.

FORMULARE.INI

[FORMULAR_PATIENT_HOME]
Title=Patient

HTML=HTML\Patient\PATIENT_HOME.html

Edit=FALSE
Database=

Inhalt

Beispielsweise:

Name
Geburtsdatum
Fallnummer
Station
Diagnosen
weitere MCDMS-/Scriptinformationen

HTML:

<div>
    <strong>[#GESAMTNAME#]</strong>
</div>

<div>
    Geburtsdatum: [#GEBDATUM#]
</div>

Mehrzeilige Diagnosen:

<div class="diagnosen">
    [#DIAGNOSEN#]
</div>

mit:

.diagnosen {
    white-space: pre-line;
}

Dynamisches HTML

Soll ein Script bewusst HTML erzeugen:

<div data-mcdms-html="INHALT"></div>

Diese Variante sollte nur verwendet werden, wenn tatsächlich HTML benötigt wird.

Für normale Textausgabe genügt:

[#VARIABLE#]

HTML

MAIN.HTML
Copy
<!doctype html>
<html lang="de">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Patientenstammblatt</title>
  <style>
    .mcdms-form-scope,.mcdms-form-scope *{box-sizing:border-box}
    .mcdms-form-scope{min-width:0;color:#202124;font-family:Arial,Helvetica,sans-serif}
    .mcdms-form-scope .patient-home{display:flex;flex-direction:column;gap:20px;max-width:1120px;margin:0}
    .mcdms-form-scope .patient-card{background:#fff;border:1px solid #d8dde9;border-radius:10px;padding:18px}
    .mcdms-form-scope .patient-header{display:grid;grid-template-columns:auto minmax(0,1fr);gap:18px;align-items:start}
    .mcdms-form-scope .patient-photo{display:block;width:96px;height:96px;object-fit:contain}
    .mcdms-form-scope .patient-name{margin:0 0 14px;font-size:1.5rem;line-height:1.25;color:#202124;overflow-wrap:anywhere}
    .mcdms-form-scope .patient-meta{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px 18px}
    .mcdms-form-scope .patient-meta-item{min-width:0}
    .mcdms-form-scope .patient-label{display:block;margin-bottom:3px;color:#4d6082;font-size:.78rem;font-weight:700;text-transform:uppercase;letter-spacing:.02em}
    .mcdms-form-scope .patient-value{display:block;min-height:1.2em;color:#202124;overflow-wrap:anywhere;white-space:pre-wrap}
    .mcdms-form-scope .detail-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:16px}
    .mcdms-form-scope .detail-group{min-width:0;background:#fff;border:1px solid #d8dde9;border-radius:10px;padding:16px}
    .mcdms-form-scope .detail-title{margin:0 0 12px;color:#4d6082;font-size:1rem}
    .mcdms-form-scope .detail-list{display:grid;gap:10px;margin:0}
    .mcdms-form-scope .detail-row{display:grid;grid-template-columns:minmax(105px,.8fr) minmax(0,1.2fr);gap:10px;border-bottom:1px solid #d8dde9;padding-bottom:8px}
    .mcdms-form-scope .detail-row:last-child{border-bottom:0;padding-bottom:0}
    .mcdms-form-scope .detail-row dt{color:#4d6082;font-size:.86rem}
    .mcdms-form-scope .detail-row dd{min-width:0;margin:0;color:#202124;font-weight:600;overflow-wrap:anywhere;white-space:pre-wrap}
    .mcdms-form-scope .dashboard-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:20px;align-items:start}
    .mcdms-form-scope .dashboard-card{min-width:0;border:1px solid #d8dde9;border-radius:10px;overflow:hidden;background:#fff}
    .mcdms-form-scope .dashboard-title{margin:0;padding:9px 12px;background:#d8dde9;color:#202124;font-size:1rem}
    .mcdms-form-scope .dashboard-content{min-height:48px;padding:12px;color:#000;line-height:1.45;white-space:pre-wrap;overflow-wrap:anywhere}
    .mcdms-form-scope .box-diagnose-aufnahme .dashboard-title{background:#f8f1c8}
    .mcdms-form-scope .box-diagnose-aktuell .dashboard-title{background:#d1effc}
    .mcdms-form-scope .box-medikation .dashboard-title{background:#eef8e6}
    .mcdms-form-scope .box-labor .dashboard-title{background:#fbede5}
    @media (max-width: 900px){.mcdms-form-scope .patient-meta,.mcdms-form-scope .detail-grid,.mcdms-form-scope .dashboard-grid{grid-template-columns:1fr}.mcdms-form-scope .patient-header{grid-template-columns:72px minmax(0,1fr)}.mcdms-form-scope .patient-photo{width:72px;height:72px}.mcdms-form-scope .patient-name{font-size:1.25rem}}
    @media print{.mcdms-form-scope .patient-card,.mcdms-form-scope .detail-group,.mcdms-form-scope .dashboard-card{break-inside:avoid;box-shadow:none}}
  </style>
</head>
<body>
  <form class="patient-home">
    <section class="patient-card patient-header" aria-label="Patient">
      <img class="patient-photo" src="/api/patient/photo" alt="Patientenfoto">
      <div>
        <h1 class="patient-name" data-field="GESAMTNAME">[#GESAMTNAME#]</h1>
        <div class="patient-meta">
          <div class="patient-meta-item"><span class="patient-label">Patient</span><span class="patient-value" data-field="PATIENTPATIENTIN">[#PATIENTPATIENTIN#]</span></div>
          <div class="patient-meta-item"><span class="patient-label">Geburtsdatum</span><span class="patient-value" data-field="GEBDATUM">[#GEBDATUM#]</span></div>
          <div class="patient-meta-item"><span class="patient-label">Alter</span><span class="patient-value" data-field="ALTER">[#ALTER#]</span></div>
          <div class="patient-meta-item"><span class="patient-label">PatientFallNr</span><span class="patient-value" data-field="PATIENTFALLNR">[#PATIENTFALLNR#]</span></div>
          <div class="patient-meta-item"><span class="patient-label">PatientID</span><span class="patient-value" data-field="PATIENTID">[#PATIENTID#]</span></div>
          <div class="patient-meta-item"><span class="patient-label">Vers.-Nr.</span><span class="patient-value" data-field="VERSNR_DRVGKV">[#VERSNR_DRVGKV#]</span></div>
        </div>
      </div>
    </section>

    <section class="detail-grid" aria-label="Patientendetails">
      <article class="detail-group">
        <h2 class="detail-title">Aufenthalt</h2>
        <dl class="detail-list">
          <div class="detail-row"><dt>Zeitraum</dt><dd data-field="BEHANDLUNGSZEITRAUM">[#BEHANDLUNGSZEITRAUM#]</dd></div>
          <div class="detail-row"><dt>Dauer</dt><dd data-field="DAUER">[#DAUER#]</dd></div>
          <div class="detail-row"><dt>Aufnahmetyp</dt><dd data-field="BEHANDLUNGSTYP">[#BEHANDLUNGSTYP#]</dd></div>
          <div class="detail-row"><dt>Nachsorge</dt><dd data-field="NACHSORGE">[#NACHSORGE#]</dd></div>
          <div class="detail-row"><dt>Nachsorge von</dt><dd data-field="NACHSORGEDATUMBEGINN">[#NACHSORGEDATUMBEGINN#]</dd></div>
          <div class="detail-row"><dt>Nachsorge bis</dt><dd data-field="NACHSORGEDATUMENDE">[#NACHSORGEDATUMENDE#]</dd></div>
        </dl>
      </article>

      <article class="detail-group">
        <h2 class="detail-title">Behandlung</h2>
        <dl class="detail-list">
          <div class="detail-row"><dt>Abteilung</dt><dd data-field="Abteilung">[#Abteilung#]</dd></div>
          <div class="detail-row"><dt>Verfahren</dt><dd data-field="VERFAHREN">[#VERFAHREN#]</dd></div>
          <div class="detail-row"><dt>Arzt/Behandler</dt><dd data-field="BEHANDLER1">[#BEHANDLER1#]</dd></div>
          <div class="detail-row"><dt>Kostenträger</dt><dd data-field="KT_NAME">[#KT_NAME#] [#KTTYP301#]</dd></div>
          <div class="detail-row"><dt>KT Telefon</dt><dd data-field="KT_TELEFON">[#KT_TELEFON#]</dd></div>
          <div class="detail-row"><dt>KT Fax</dt><dd data-field="KT_TELEFAX">[#KT_TELEFAX#]</dd></div>
        </dl>
      </article>

      <article class="detail-group">
        <h2 class="detail-title">Kontakt</h2>
        <dl class="detail-list">
          <div class="detail-row"><dt>Telefon</dt><dd data-field="TELEFON">[#TELEFON#]</dd></div>
          <div class="detail-row"><dt>Mobil</dt><dd data-field="MOBIL">[#MOBIL#]</dd></div>
          <div class="detail-row"><dt>E-Mail</dt><dd data-field="EMAIL">[#EMAIL#]</dd></div>
        </dl>
      </article>
    </section>

    <section class="dashboard-grid" aria-label="Patientenübersicht">
      <article class="dashboard-card box-diagnose-aufnahme">
        <h2 class="dashboard-title">Aufnahmediagnosen</h2>
        <div class="dashboard-content" data-field="AUFNAHMEDIAGNOSEN">[#AUFDIAGNOSEGESAMT#]</div>
      </article>
      <article class="dashboard-card box-diagnose-aktuell">
        <h2 class="dashboard-title">Aktuelle Diagnosen (Entlassung)</h2>
        <div class="dashboard-content" data-field="ENTLASSDIAGNOSEN">[#DIAGNOSEGESAMT#]</div>
      </article>
      <article class="dashboard-card box-medikation">
        <h2 class="dashboard-title">Aktuelle Medikation</h2>
        <div class="dashboard-content" data-field="AKTUELLEMEDIKATION">[#AKTUELLEMEDIKATION#]</div>
      </article>
      <article class="dashboard-card box-labor">
        <h2 class="dashboard-title">Letztes Labor</h2>
        <div class="dashboard-content" data-field="LETZTESLABOR">[#LETZTESLABOR#]</div>
      </article>
    </section>
  </form>
</body>
</html>

8. Formular mit MCDMS-Rechten

Ziel

Dieses Beispiel zeigt ein normales klinisches Formular, dessen Berechtigung vollständig aus MCDMS stammt.

FORMULARE.INI

[FORMULAR_BEHANDLUNG]
Title=Behandlung

HTML=HTML\Arzt\BEHANDLUNG.html

Edit=TRUE
MultiRecord=TRUE
Delete=TRUE
AllowDuplicate=TRUE

Database=FRM_BEHANDLUNG;FRMID;PatientID

MATCHCODE=BEHANDLUNG

Rechte

Die WebAkte ermittelt das effektive Recht über MCDMS.

-1 = gesperrt
 0 = kein Zugriff
 1 = lesen
 2 = lesen / erstellen / bearbeiten
 3 = Vollzugriff

Für das Löschen gelten zusätzlich die Owner-Regeln.

Die HTML-Datei selbst enthält keinerlei Rechteprüfung.

Das ist wichtig:

HTML
→ Darstellung

MCDMS / WebServer
→ Berechtigung

Alternative: Nur Administrator

AdminOnly=TRUE

Alternative: Bestimmte Benutzer

ONLY_USERS=MUELLER;MEIER;SCHMIDT

Diese Varianten sind für Webformulare ohne MCDMS-MATCHCODE vorgesehen.

HTML

Accordion: Vollständiges HTML – Formular mit MCDMS-Rechten

9. Referenzformular für Standard-Controls

Ziel

Dieses Beispiel dient nicht als fachliches Klinikformular, sondern als kompakte Vorlage für die normalen HTML-Controltypen.

Enthalten sind beispielsweise:

Text
Textarea
Datum
Uhrzeit
Datum/Uhrzeit
Zahl
Checkbox
Radio
Select
Readonly
Hidden
Anzeigevariable
Action-Button
responsive Zwei-Spalten-Darstellung

Datenmodell

Beispielsweise:

FRMID
PatientID
TEXTFELD
READONLYFELD
DATUM
UHRZEIT
ZEITPUNKT
ZAHL
AUSWAHL
CHECKBOX
RADIOWERT
LANGTEXT
VERSTECKT

FORMULARE.INI

[FORMULAR_REFERENZ]
Title=HTML Referenzformular

HTML=HTML\Beispiele\REFERENZ.html

Edit=TRUE
MultiRecord=TRUE
Delete=TRUE
AllowDuplicate=TRUE
DoubleSaveButton=TRUE

Database=FRM_REFERENZ;FRMID;PatientID
OrderBy=FRMID;DESC

Action_BEISPIEL=CONFIG\Scripts\WEB\Beispiel.MCFSCR

HTML

MAIN.HTML
Copy
<!doctype html>
<html lang="de">

<head>
    <meta charset="utf-8">

    <meta name="viewport"
          content="width=device-width, initial-scale=1.0">

    <title>HTML Referenzformular</title>

    <style>
        body {
            margin: 0;
            padding: 0;
            background: transparent;
        }

        .referenz-form,
        .referenz-form * {
            box-sizing: border-box;
        }

        .referenz-form {
            width: 100%;
            max-width: 1100px;
            margin: 0 auto;
            padding: 20px;

            font-family:
                "Segoe UI",
                Arial,
                sans-serif;

            color: #1f2937;
        }

        .referenz-card {
            background: #ffffff;

            border: 1px solid #d8e0e8;
            border-radius: 10px;

            padding: 20px;
        }

        .referenz-title {
            margin: 0 0 18px;
            font-size: 22px;
            font-weight: 600;
        }

        .referenz-patient {
            margin-bottom: 20px;
            color: #475569;
            font-size: 14px;
        }

        .referenz-grid {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 16px;
        }

        .referenz-form label {
            display: grid;
            gap: 6px;

            font-size: 14px;
            font-weight: 600;
        }

        .referenz-form input[type="text"],
        .referenz-form input[type="date"],
        .referenz-form input[type="time"],
        .referenz-form input[type="datetime-local"],
        .referenz-form input[type="number"],
        .referenz-form select,
        .referenz-form textarea {
            width: 100%;

            padding: 8px 10px;

            background: #ffffff;
            color: #1f2937;

            border: 1px solid #cbd5e1;
            border-radius: 6px;

            font: inherit;
            font-weight: 400;
        }

        .referenz-form textarea {
            min-height: 130px;
            resize: vertical;
        }

        .referenz-full {
            grid-column: 1 / -1;
        }

        .referenz-options {
            display: flex;
            flex-wrap: wrap;
            gap: 14px;
        }

        .referenz-option {
            display: inline-flex !important;
            grid-template-columns: none !important;
            align-items: center;
            gap: 7px !important;

            font-weight: 400 !important;
        }

        .referenz-option input {
            margin: 0;
        }

        .referenz-info {
            margin-top: 18px;
            padding: 12px 14px;

            background: #f8fafc;
            border: 1px solid #e2e8f0;
            border-radius: 8px;
        }

        .referenz-actions {
            display: flex;
            flex-wrap: wrap;
            gap: 8px;

            margin-top: 18px;
        }

        .referenz-actions button {
            padding: 8px 14px;

            border: 1px solid #9eb9dc;
            border-radius: 6px;

            background: #ffffff;
            color: #2b6cb0;

            cursor: pointer;
            font: inherit;
        }

        .referenz-actions button:hover {
            background: #eef5fc;
        }

        @media (max-width: 700px) {
            .referenz-grid {
                grid-template-columns: 1fr;
            }

            .referenz-full {
                grid-column: auto;
            }

            .referenz-form {
                padding: 10px;
            }
        }
    </style>
</head>

<body>

<form class="referenz-form">

    <input type="hidden"
           name="VERSTECKT"
           data-field="VERSTECKT"
           value="[#VERSTECKT#]">

    <div class="referenz-card">

        <h2 class="referenz-title">
            HTML Referenzformular
        </h2>

        <div class="referenz-patient">
            Patient:
            <strong>[#GESAMTNAME#]</strong>
            <br>
            Geburtsdatum:
            <strong>[#GEBDATUM#]</strong>
        </div>

        <div class="referenz-grid">

            <label>
                Textfeld

                <input type="text"
                       name="TEXTFELD"
                       data-field="TEXTFELD">
            </label>

            <label>
                Readonly-Feld

                <input type="text"
                       name="READONLYFELD"
                       data-field="READONLYFELD"
                       readonly>
            </label>

            <label>
                Datum

                <input type="date"
                       name="DATUM"
                       data-field="DATUM">
            </label>

            <label>
                Uhrzeit

                <input type="time"
                       name="UHRZEIT"
                       data-field="UHRZEIT">
            </label>

            <label>
                Datum und Uhrzeit

                <input type="datetime-local"
                       name="ZEITPUNKT"
                       data-field="ZEITPUNKT">
            </label>

            <label>
                Zahl

                <input type="number"
                       name="ZAHL"
                       data-field="ZAHL"
                       min="0"
                       step="1">
            </label>

            <label>
                Auswahl

                <select name="AUSWAHL"
                        data-field="AUSWAHL">

                    <option value="">
                        Bitte wählen
                    </option>

                    <option value="A">
                        Auswahl A
                    </option>

                    <option value="B">
                        Auswahl B
                    </option>

                    <option value="C">
                        Auswahl C
                    </option>

                </select>
            </label>

            <div>
                <div style="margin-bottom:6px;font-size:14px;font-weight:600;">
                    Checkbox
                </div>

                <label class="referenz-option">
                    <input type="checkbox"
                           name="CHECKBOX"
                           data-field="CHECKBOX">

                    Aktiv
                </label>
            </div>

            <div class="referenz-full">

                <div style="margin-bottom:6px;font-size:14px;font-weight:600;">
                    Radio-Auswahl
                </div>

                <div class="referenz-options">

                    <label class="referenz-option">
                        <input type="radio"
                               name="RADIOWERT"
                               data-field="RADIOWERT"
                               value="A">

                        Option A
                    </label>

                    <label class="referenz-option">
                        <input type="radio"
                               name="RADIOWERT"
                               data-field="RADIOWERT"
                               value="B">

                        Option B
                    </label>

                    <label class="referenz-option">
                        <input type="radio"
                               name="RADIOWERT"
                               data-field="RADIOWERT"
                               value="C">

                        Option C
                    </label>

                </div>

            </div>

            <label class="referenz-full">
                Mehrzeiliger Text

                <textarea name="LANGTEXT"
                          data-field="LANGTEXT"></textarea>
            </label>

        </div>

        <div class="referenz-info">

            <strong>Anzeige ohne Speicherung:</strong>

            <div>
                PatientID:
                [#PATIENTID#]
            </div>

            <div>
                Aktueller Benutzer:
                [#USERID#]
            </div>

        </div>

        <div class="referenz-actions">

            <button type="button"
                    data-mcdms-action="BEISPIEL">
                Beispiel-Aktion
            </button>

            <button type="button"
                    id="setTextButton">
                Text per JavaScript setzen
            </button>

        </div>

    </div>

</form>

<script>
(function () {

    document.addEventListener('click', function (event) {

        const button =
            event.target.closest('#setTextButton');

        if (!button) {
            return;
        }

        const field =
            document.querySelector(
                '[data-field="TEXTFELD"]'
            );

        if (!field) {
            return;
        }

        field.value = 'Beispielwert';

        field.dispatchEvent(
            new Event('input', {
                bubbles: true
            })
        );

        field.dispatchEvent(
            new Event('change', {
                bubbles: true
            })
        );
    });

})();
</script>

</body>
</html>

Dieses Beispiel ist besonders geeignet, wenn ein neues Formular begonnen werden soll und zunächst eine saubere technische Vorlage benötigt wird.

10. Welches Beispiel eignet sich wofür?

Zum Abschluss würde ich nur noch eine kleine Übersicht machen:

AnforderungGeeignetes Beispiel
Einfaches Formular mit mehreren DatensätzenVerlaufsdokumentation
Viele Datensätze gleichzeitig bearbeitenDiagnosen
Mehrere fachliche Listen in einem LIST-FormularDiagnosen
Persistente frei änderbare ReihenfolgeDiagnosen
Master mit wiederholbaren UntereinträgenMedikationsplan / Leistungserfassung
Mehrere sichtbare DetailbereicheMedikationsplan
Eigene Bedienlogik mit AuswahlbuttonsLeistungserfassung
Access statt SQL ServerAccess-Formular
MCDMS-Scripts und ActionsScripts und Actions
Reine InformationsseitePatientenübersicht
MCDMS-RechteFormular mit MATCHCODE
Vorlage für normale HTML-ControlsReferenzformular