mirror of
https://github.com/Sudo-JHare/FHIRFLARE-IG-Toolkit.git
synced 2025-06-15 17:20:00 +00:00
Updated, cliboard copy buttons on examples, added new UI elements, updated and added more validation for samples, and switched validator to ajax
282 lines
13 KiB
HTML
282 lines
13 KiB
HTML
<!-- templates/validate_sample.html -->
|
|
{% extends "base.html" %}
|
|
|
|
{% block content %}
|
|
<div class="px-4 py-5 my-5 text-center">
|
|
<img class="d-block mx-auto mb-4" src="{{ url_for('static', filename='FHIRFLARE.png') }}" alt="FHIRFLARE IG Toolkit" width="192" height="192">
|
|
<h1 class="display-5 fw-bold text-body-emphasis">Validate FHIR Sample</h1>
|
|
<div class="col-lg-6 mx-auto">
|
|
<p class="lead mb-4">
|
|
Validate a FHIR resource or bundle against a selected Implementation Guide. (ALPHA - TEST Fhir pathing is complex and the logic is WIP, please report anomalies you find in Github issues alongside your sample json REMOVE PHI)
|
|
</p>
|
|
<div class="d-grid gap-2 d-sm-flex justify-content-sm-center">
|
|
<a href="{{ url_for('index') }}" class="btn btn-primary btn-lg px-4 gap-3">Back to Home</a>
|
|
<a href="{{ url_for('view_igs') }}" class="btn btn-outline-secondary btn-lg px-4">Manage FHIR Packages</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="container mt-4">
|
|
<div class="card">
|
|
<div class="card-header">Validation Form</div>
|
|
<div class="card-body">
|
|
<form id="validationForm">
|
|
{{ form.hidden_tag() }}
|
|
<div class="mb-3">
|
|
<small class="form-text text-muted">
|
|
Select a package from the list below or enter a new one (e.g., <code>hl7.fhir.us.core</code>).
|
|
</small>
|
|
<div class="mt-2">
|
|
<select class="form-select" id="packageSelect">
|
|
<option value="">-- Select a Package --</option>
|
|
{% if packages %}
|
|
{% for pkg in packages %}
|
|
<option value="{{ pkg.name }}#{{ pkg.version }}">{{ pkg.name }}#{{ pkg.version }}</option>
|
|
{% endfor %}
|
|
{% else %}
|
|
<option value="" disabled>No packages available</option>
|
|
{% endif %}
|
|
</select>
|
|
</div>
|
|
<label for="{{ form.package_name.id }}" class="form-label">Package Name</label>
|
|
{{ form.package_name(class="form-control", id=form.package_name.id) }}
|
|
{% for error in form.package_name.errors %}
|
|
<div class="text-danger">{{ error }}</div>
|
|
{% endfor %}
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="{{ form.version.id }}" class="form-label">Package Version</label>
|
|
{{ form.version(class="form-control", id=form.version.id) }}
|
|
{% for error in form.version.errors %}
|
|
<div class="text-danger">{{ error }}</div>
|
|
{% endfor %}
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="{{ form.include_dependencies.id }}" class="form-label">Include Dependencies</label>
|
|
<div class="form-check">
|
|
{{ form.include_dependencies(class="form-check-input") }}
|
|
{{ form.include_dependencies.label(class="form-check-label") }}
|
|
</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="{{ form.mode.id }}" class="form-label">Validation Mode</label>
|
|
{{ form.mode(class="form-select") }}
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="{{ form.sample_input.id }}" class="form-label">Sample FHIR Resource/Bundle (JSON)</label>
|
|
{{ form.sample_input(class="form-control", rows=10, placeholder="Paste your FHIR JSON here...") }}
|
|
{% for error in form.sample_input.errors %}
|
|
<div class="text-danger">{{ error }}</div>
|
|
{% endfor %}
|
|
</div>
|
|
<button type="button" class="btn btn-primary" id="validateButton">Validate</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card mt-4" id="validationResult" style="display: none;">
|
|
<div class="card-header">Validation Report</div>
|
|
<div class="card-body" id="validationContent">
|
|
<!-- Results will be populated dynamically -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const packageSelect = document.getElementById('packageSelect');
|
|
const packageNameInput = document.getElementById('{{ form.package_name.id }}');
|
|
const versionInput = document.getElementById('{{ form.version.id }}');
|
|
const validateButton = document.getElementById('validateButton');
|
|
const sampleInput = document.getElementById('{{ form.sample_input.id }}');
|
|
const validationResult = document.getElementById('validationResult');
|
|
const validationContent = document.getElementById('validationContent');
|
|
|
|
// Debug DOM elements
|
|
console.log('Package Select:', packageSelect ? 'Found' : 'Missing');
|
|
console.log('Package Name Input:', packageNameInput ? 'Found' : 'Missing');
|
|
console.log('Version Input:', versionInput ? 'Found' : 'Missing');
|
|
|
|
// Update package_name and version fields from dropdown
|
|
function updatePackageFields(select) {
|
|
const value = select.value;
|
|
console.log('Selected package:', value);
|
|
|
|
if (!packageNameInput || !versionInput) {
|
|
console.error('Input fields not found');
|
|
return;
|
|
}
|
|
|
|
if (value) {
|
|
const [name, version] = value.split('#');
|
|
if (name && version) {
|
|
packageNameInput.value = name;
|
|
versionInput.value = version;
|
|
console.log('Updated fields:', { packageName: name, version });
|
|
} else {
|
|
console.warn('Invalid package format:', value);
|
|
packageNameInput.value = '';
|
|
versionInput.value = '';
|
|
}
|
|
} else {
|
|
packageNameInput.value = '';
|
|
versionInput.value = '';
|
|
console.log('Cleared fields: no package selected');
|
|
}
|
|
}
|
|
|
|
// Initialize dropdown based on form values
|
|
if (packageNameInput && versionInput && packageNameInput.value && versionInput.value) {
|
|
const currentValue = `${packageNameInput.value}#${versionInput.value}`;
|
|
if (packageSelect.querySelector(`option[value="${currentValue}"]`)) {
|
|
packageSelect.value = currentValue;
|
|
console.log('Initialized dropdown with:', currentValue);
|
|
}
|
|
}
|
|
|
|
// Debug dropdown options
|
|
console.log('Dropdown options:', Array.from(packageSelect.options).map(opt => opt.value));
|
|
|
|
// Attach event listener for package selection
|
|
if (packageSelect) {
|
|
packageSelect.addEventListener('change', function() {
|
|
updatePackageFields(this);
|
|
});
|
|
}
|
|
|
|
// Client-side JSON validation preview
|
|
if (sampleInput) {
|
|
sampleInput.addEventListener('input', function() {
|
|
try {
|
|
if (this.value.trim()) {
|
|
JSON.parse(this.value);
|
|
this.classList.remove('is-invalid');
|
|
this.classList.add('is-valid');
|
|
} else {
|
|
this.classList.remove('is-valid', 'is-invalid');
|
|
}
|
|
} catch (e) {
|
|
this.classList.remove('is-valid');
|
|
this.classList.add('is-invalid');
|
|
}
|
|
});
|
|
}
|
|
|
|
// Validate button handler
|
|
if (validateButton) {
|
|
validateButton.addEventListener('click', function() {
|
|
const packageName = packageNameInput.value;
|
|
const version = versionInput.value;
|
|
const sampleData = sampleInput.value.trim();
|
|
|
|
if (!packageName || !version) {
|
|
validationResult.style.display = 'block';
|
|
validationContent.innerHTML = '<div class="alert alert-danger">Please select a package and version.</div>';
|
|
return;
|
|
}
|
|
|
|
if (!sampleData) {
|
|
validationResult.style.display = 'block';
|
|
validationContent.innerHTML = '<div class="alert alert-danger">Please provide FHIR sample JSON.</div>';
|
|
return;
|
|
}
|
|
|
|
try {
|
|
JSON.parse(sampleData);
|
|
} catch (e) {
|
|
validationResult.style.display = 'block';
|
|
validationContent.innerHTML = '<div class="alert alert-danger">Invalid JSON: ' + e.message + '</div>';
|
|
return;
|
|
}
|
|
|
|
validationResult.style.display = 'block';
|
|
validationContent.innerHTML = '<div class="text-center py-3"><div class="spinner-border text-primary" role="status"><span class="visually-hidden">Validating...</span></div></div>';
|
|
|
|
console.log('Sending request to /api/validate-sample with:', {
|
|
package_name: packageName,
|
|
version: version,
|
|
sample_data_length: sampleData.length
|
|
});
|
|
|
|
fetch('/api/validate-sample', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRFToken': '{{ form.csrf_token._value() }}'
|
|
},
|
|
body: JSON.stringify({
|
|
package_name: packageName,
|
|
version: version,
|
|
sample_data: sampleData
|
|
})
|
|
})
|
|
.then(response => {
|
|
console.log('Server response status:', response.status, response.statusText);
|
|
if (!response.ok) {
|
|
return response.text().then(text => {
|
|
console.error('Server response text:', text.substring(0, 200) + (text.length > 200 ? '...' : ''));
|
|
throw new Error(`HTTP error ${response.status}: ${text.substring(0, 100)}...`);
|
|
});
|
|
}
|
|
return response.json();
|
|
})
|
|
.then(data => {
|
|
console.log('Validation response:', data);
|
|
validationContent.innerHTML = '';
|
|
let hasContent = false;
|
|
|
|
if (data.errors && data.errors.length > 0) {
|
|
hasContent = true;
|
|
const errorDiv = document.createElement('div');
|
|
errorDiv.className = 'alert alert-danger';
|
|
errorDiv.innerHTML = '<h5>Errors</h5><ul>' + data.errors.map(e => `<li>${e}</li>`).join('') + '</ul>';
|
|
validationContent.appendChild(errorDiv);
|
|
}
|
|
|
|
if (data.warnings && data.warnings.length > 0) {
|
|
hasContent = true;
|
|
const warningDiv = document.createElement('div');
|
|
warningDiv.className = 'alert alert-warning';
|
|
warningDiv.innerHTML = '<h5>Warnings</h5><ul>' + data.warnings.map(w => `<li>${w}</li>`).join('') + '</ul>';
|
|
validationContent.appendChild(warningDiv);
|
|
}
|
|
|
|
if (data.results) {
|
|
hasContent = true;
|
|
const resultsDiv = document.createElement('div');
|
|
resultsDiv.innerHTML = '<h5>Detailed Results</h5>';
|
|
for (const [resourceId, result] of Object.entries(data.results)) {
|
|
resultsDiv.innerHTML += `
|
|
<h6>${resourceId}</h6>
|
|
<p><strong>Valid:</strong> ${result.valid ? 'Yes' : 'No'}</p>
|
|
`;
|
|
if (result.errors && result.errors.length > 0) {
|
|
resultsDiv.innerHTML += '<ul class="text-danger">' +
|
|
result.errors.map(e => `<li>${e}</li>`).join('') + '</ul>';
|
|
}
|
|
if (result.warnings && result.warnings.length > 0) {
|
|
resultsDiv.innerHTML += '<ul class="text-warning">' +
|
|
result.warnings.map(w => `<li>${w}</li>`).join('') + '</ul>';
|
|
}
|
|
}
|
|
validationContent.appendChild(resultsDiv);
|
|
}
|
|
|
|
const summaryDiv = document.createElement('div');
|
|
summaryDiv.className = 'alert alert-info';
|
|
summaryDiv.innerHTML = `<h5>Summary</h5><p>Valid: ${data.valid ? 'Yes' : 'No'}</p>`;
|
|
validationContent.appendChild(summaryDiv);
|
|
|
|
if (!hasContent && data.valid) {
|
|
validationContent.innerHTML = '<div class="alert alert-success">Validation passed with no errors or warnings.</div>';
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Validation error:', error);
|
|
validationContent.innerHTML = '<div class="alert alert-danger">Error during validation: ' + error.message + '</div>';
|
|
});
|
|
});
|
|
}
|
|
});
|
|
</script>
|
|
{% endblock %} |