hospital-management/templates/integration/systems/external_system_form.html
2025-08-12 13:33:25 +03:00

676 lines
30 KiB
HTML

{% extends "base.html" %}
{% load static %}
{% block title %}{% if object %}Edit System{% else %}New System{% endif %} - {{ block.super }}{% endblock %}
{% block content %}
<div class="container-fluid">
<!-- Page Header -->
<div class="d-flex justify-content-between align-items-center mb-4">
<div>
<h1 class="h3 mb-1">
{% if object %}
<i class="fas fa-edit me-2"></i>Edit External System
{% else %}
<i class="fas fa-plus me-2"></i>New External System
{% endif %}
</h1>
<nav aria-label="breadcrumb">
<ol class="breadcrumb mb-0">
<li class="breadcrumb-item"><a href="{% url 'integration:dashboard' %}">Integration</a></li>
<li class="breadcrumb-item"><a href="{% url 'integration:external_system_list' %}">External Systems</a></li>
{% if object %}
<li class="breadcrumb-item"><a href="{% url 'integration:external_system_detail' object.system_id %}">{{ object.name }}</a></li>
<li class="breadcrumb-item active">Edit</li>
{% else %}
<li class="breadcrumb-item active">New</li>
{% endif %}
</ol>
</nav>
</div>
<div class="btn-group">
<a href="{% if object %}{% url 'integration:external_system_detail' object.system_id %}{% else %}{% url 'integration:external_system_list' %}{% endif %}" class="btn btn-outline-secondary">
<i class="fas fa-arrow-left me-2"></i>Back
</a>
</div>
</div>
<form method="post" novalidate>
{% csrf_token %}
<div class="row">
<!-- Main Form -->
<div class="col-lg-8">
<!-- Basic Information -->
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0">
<i class="fas fa-info-circle me-2"></i>Basic Information
</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label for="{{ form.name.id_for_label }}" class="form-label">
System Name <span class="text-danger">*</span>
</label>
{{ form.name }}
{% if form.name.errors %}
<div class="invalid-feedback d-block">
{{ form.name.errors.0 }}
</div>
{% endif %}
<div class="form-text">Descriptive name for the external system</div>
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="{{ form.system_type.id_for_label }}" class="form-label">
System Type <span class="text-danger">*</span>
</label>
{{ form.system_type }}
{% if form.system_type.errors %}
<div class="invalid-feedback d-block">
{{ form.system_type.errors.0 }}
</div>
{% endif %}
</div>
</div>
</div>
<div class="mb-3">
<label for="{{ form.description.id_for_label }}" class="form-label">
Description
</label>
{{ form.description }}
{% if form.description.errors %}
<div class="invalid-feedback d-block">
{{ form.description.errors.0 }}
</div>
{% endif %}
<div class="form-text">Detailed description of the system and its purpose</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label for="{{ form.vendor.id_for_label }}" class="form-label">
Vendor
</label>
{{ form.vendor }}
{% if form.vendor.errors %}
<div class="invalid-feedback d-block">
{{ form.vendor.errors.0 }}
</div>
{% endif %}
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="{{ form.version.id_for_label }}" class="form-label">
Version
</label>
{{ form.version }}
{% if form.version.errors %}
<div class="invalid-feedback d-block">
{{ form.version.errors.0 }}
</div>
{% endif %}
</div>
</div>
</div>
</div>
</div>
<!-- Connection Details -->
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0">
<i class="fas fa-plug me-2"></i>Connection Details
</h5>
</div>
<div class="card-body">
<div class="mb-3">
<label for="{{ form.base_url.id_for_label }}" class="form-label">
Base URL
</label>
{{ form.base_url }}
{% if form.base_url.errors %}
<div class="invalid-feedback d-block">
{{ form.base_url.errors.0 }}
</div>
{% endif %}
<div class="form-text">Full URL for API-based systems (e.g., https://api.example.com)</div>
</div>
<div class="row">
<div class="col-md-8">
<div class="mb-3">
<label for="{{ form.host.id_for_label }}" class="form-label">
Host
</label>
{{ form.host }}
{% if form.host.errors %}
<div class="invalid-feedback d-block">
{{ form.host.errors.0 }}
</div>
{% endif %}
<div class="form-text">Hostname or IP address for direct connections</div>
</div>
</div>
<div class="col-md-4">
<div class="mb-3">
<label for="{{ form.port.id_for_label }}" class="form-label">
Port
</label>
{{ form.port }}
{% if form.port.errors %}
<div class="invalid-feedback d-block">
{{ form.port.errors.0 }}
</div>
{% endif %}
</div>
</div>
</div>
<div class="mb-3">
<label for="{{ form.database_name.id_for_label }}" class="form-label">
Database Name
</label>
{{ form.database_name }}
{% if form.database_name.errors %}
<div class="invalid-feedback d-block">
{{ form.database_name.errors.0 }}
</div>
{% endif %}
<div class="form-text">Database name for database connections</div>
</div>
</div>
</div>
<!-- Authentication -->
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0">
<i class="fas fa-lock me-2"></i>Authentication
</h5>
</div>
<div class="card-body">
<div class="mb-3">
<label for="{{ form.authentication_type.id_for_label }}" class="form-label">
Authentication Type <span class="text-danger">*</span>
</label>
{{ form.authentication_type }}
{% if form.authentication_type.errors %}
<div class="invalid-feedback d-block">
{{ form.authentication_type.errors.0 }}
</div>
{% endif %}
</div>
<div id="auth-config-section">
<div class="mb-3">
<label for="auth_config" class="form-label">
Authentication Configuration
</label>
<textarea id="auth_config" name="authentication_config" class="form-control" rows="6"
placeholder='{"username": "your_username", "password": "your_password"}'
>{{ form.authentication_config.value|default:"" }}</textarea>
{% if form.authentication_config.errors %}
<div class="invalid-feedback d-block">
{{ form.authentication_config.errors.0 }}
</div>
{% endif %}
<div class="form-text">JSON configuration for authentication parameters</div>
</div>
</div>
</div>
</div>
<!-- Configuration -->
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0">
<i class="fas fa-cogs me-2"></i>System Configuration
</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-4">
<div class="mb-3">
<label for="{{ form.timeout_seconds.id_for_label }}" class="form-label">
Timeout (seconds)
</label>
{{ form.timeout_seconds }}
{% if form.timeout_seconds.errors %}
<div class="invalid-feedback d-block">
{{ form.timeout_seconds.errors.0 }}
</div>
{% endif %}
</div>
</div>
<div class="col-md-4">
<div class="mb-3">
<label for="{{ form.retry_attempts.id_for_label }}" class="form-label">
Retry Attempts
</label>
{{ form.retry_attempts }}
{% if form.retry_attempts.errors %}
<div class="invalid-feedback d-block">
{{ form.retry_attempts.errors.0 }}
</div>
{% endif %}
</div>
</div>
<div class="col-md-4">
<div class="mb-3">
<label for="{{ form.retry_delay_seconds.id_for_label }}" class="form-label">
Retry Delay (seconds)
</label>
{{ form.retry_delay_seconds }}
{% if form.retry_delay_seconds.errors %}
<div class="invalid-feedback d-block">
{{ form.retry_delay_seconds.errors.0 }}
</div>
{% endif %}
</div>
</div>
</div>
<div class="mb-3">
<label for="system_config" class="form-label">
System Configuration
</label>
<textarea id="system_config" name="configuration" class="form-control" rows="8"
placeholder='{"api_version": "v1", "format": "json", "compression": true}'
>{{ form.configuration.value|default:"" }}</textarea>
{% if form.configuration.errors %}
<div class="invalid-feedback d-block">
{{ form.configuration.errors.0 }}
</div>
{% endif %}
<div class="form-text">JSON configuration for system-specific parameters</div>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="is_active" name="is_active"
{% if form.is_active.value %}checked{% endif %}>
<label class="form-check-label" for="is_active">
System is active and available for integrations
</label>
</div>
</div>
</div>
</div>
<!-- Sidebar -->
<div class="col-lg-4">
<!-- Form Actions -->
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0">
<i class="fas fa-save me-2"></i>Actions
</h5>
</div>
<div class="card-body">
<div class="d-grid gap-2">
<button type="submit" class="btn btn-primary">
<i class="fas fa-save me-2"></i>
{% if object %}Update System{% else %}Create System{% endif %}
</button>
{% if object %}
<button type="button" class="btn btn-outline-info" onclick="testConnection()">
<i class="fas fa-plug me-2"></i>Test Connection
</button>
{% endif %}
<a href="{% if object %}{% url 'integration:external_system_detail' object.system_id %}{% else %}{% url 'integration:external_system_list' %}{% endif %}" class="btn btn-outline-secondary">
<i class="fas fa-times me-2"></i>Cancel
</a>
<button type="button" class="btn btn-outline-warning" onclick="clearForm()">
<i class="fas fa-eraser me-2"></i>Clear Form
</button>
</div>
</div>
</div>
<!-- System Type Guide -->
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0">
<i class="fas fa-question-circle me-2"></i>System Types
</h5>
</div>
<div class="card-body">
<div class="accordion" id="systemTypeGuide">
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#healthcareTypes">
Healthcare Systems
</button>
</h2>
<div id="healthcareTypes" class="accordion-collapse collapse" data-bs-parent="#systemTypeGuide">
<div class="accordion-body">
<ul class="list-unstyled mb-0">
<li><strong>EHR:</strong> Electronic Health Records</li>
<li><strong>HIS:</strong> Hospital Information System</li>
<li><strong>LIS:</strong> Laboratory Information System</li>
<li><strong>RIS:</strong> Radiology Information System</li>
<li><strong>PACS:</strong> Picture Archiving System</li>
<li><strong>Pharmacy:</strong> Pharmacy Management</li>
</ul>
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#technicalTypes">
Technical Systems
</button>
</h2>
<div id="technicalTypes" class="accordion-collapse collapse" data-bs-parent="#systemTypeGuide">
<div class="accordion-body">
<ul class="list-unstyled mb-0">
<li><strong>API:</strong> REST/SOAP API Services</li>
<li><strong>Database:</strong> Direct database connections</li>
<li><strong>File:</strong> File-based integrations</li>
<li><strong>FTP/SFTP:</strong> File transfer protocols</li>
<li><strong>Cloud:</strong> Cloud services</li>
<li><strong>IoT:</strong> Internet of Things devices</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Authentication Guide -->
<div class="card">
<div class="card-header">
<h5 class="mb-0">
<i class="fas fa-shield-alt me-2"></i>Authentication Guide
</h5>
</div>
<div class="card-body">
<div class="accordion" id="authGuide">
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#basicAuth">
Basic Authentication
</button>
</h2>
<div id="basicAuth" class="accordion-collapse collapse" data-bs-parent="#authGuide">
<div class="accordion-body">
<code>
{<br>
&nbsp;&nbsp;"username": "your_username",<br>
&nbsp;&nbsp;"password": "your_password"<br>
}
</code>
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#apiKeyAuth">
API Key
</button>
</h2>
<div id="apiKeyAuth" class="accordion-collapse collapse" data-bs-parent="#authGuide">
<div class="accordion-body">
<code>
{<br>
&nbsp;&nbsp;"api_key": "your_api_key",<br>
&nbsp;&nbsp;"header_name": "X-API-Key"<br>
}
</code>
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#oauth2Auth">
OAuth 2.0
</button>
</h2>
<div id="oauth2Auth" class="accordion-collapse collapse" data-bs-parent="#authGuide">
<div class="accordion-body">
<code>
{<br>
&nbsp;&nbsp;"client_id": "your_client_id",<br>
&nbsp;&nbsp;"client_secret": "your_secret",<br>
&nbsp;&nbsp;"token_url": "https://auth.example.com/token",<br>
&nbsp;&nbsp;"scope": "read write"<br>
}
</code>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</form>
</div>
<script>
// External system form functionality
document.addEventListener('DOMContentLoaded', function() {
// Add Bootstrap classes to form elements
const form = document.querySelector('form');
const inputs = form.querySelectorAll('input, select, textarea');
inputs.forEach(input => {
if (!input.classList.contains('form-control') && !input.classList.contains('form-select') && !input.classList.contains('form-check-input')) {
if (input.tagName === 'SELECT') {
input.classList.add('form-select');
} else if (input.type !== 'checkbox') {
input.classList.add('form-control');
}
}
});
// Authentication type change handler
const authTypeSelect = document.getElementById('id_authentication_type');
if (authTypeSelect) {
authTypeSelect.addEventListener('change', updateAuthConfigPlaceholder);
updateAuthConfigPlaceholder(); // Initial call
}
// JSON validation for configuration fields
const jsonFields = ['auth_config', 'system_config'];
jsonFields.forEach(fieldId => {
const field = document.getElementById(fieldId);
if (field) {
field.addEventListener('blur', function() {
validateJSON(this);
});
}
});
});
function updateAuthConfigPlaceholder() {
const authType = document.getElementById('id_authentication_type').value;
const authConfig = document.getElementById('auth_config');
const placeholders = {
'basic': '{"username": "your_username", "password": "your_password"}',
'bearer': '{"token": "your_bearer_token"}',
'api_key': '{"api_key": "your_api_key", "header_name": "X-API-Key"}',
'oauth2': '{"client_id": "your_client_id", "client_secret": "your_secret", "token_url": "https://auth.example.com/token", "scope": "read write"}',
'certificate': '{"cert_file": "/path/to/cert.pem", "key_file": "/path/to/key.pem"}',
'custom': '{"custom_param": "value"}'
};
if (authConfig && placeholders[authType]) {
authConfig.placeholder = placeholders[authType];
}
}
function validateJSON(field) {
const value = field.value.trim();
if (value === '') {
field.classList.remove('is-invalid', 'is-valid');
return;
}
try {
JSON.parse(value);
field.classList.remove('is-invalid');
field.classList.add('is-valid');
} catch (e) {
field.classList.remove('is-valid');
field.classList.add('is-invalid');
// Show error message
let errorDiv = field.parentNode.querySelector('.json-error');
if (!errorDiv) {
errorDiv = document.createElement('div');
errorDiv.className = 'invalid-feedback json-error';
field.parentNode.appendChild(errorDiv);
}
errorDiv.textContent = 'Invalid JSON format: ' + e.message;
}
}
function testConnection() {
const systemId = '{{ object.system_id }}';
fetch(`{% url 'integration:test_connection' 0 %}`.replace('0', systemId), {
method: 'POST',
headers: {
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value,
'Content-Type': 'application/json',
},
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('Connection test successful!\n\nResponse time: ' + data.response_time + 'ms');
} else {
alert('Connection test failed:\n\n' + data.error);
}
})
.catch(error => {
console.error('Error testing connection:', error);
alert('Error testing connection: ' + error.message);
});
}
function clearForm() {
if (confirm('Are you sure you want to clear all form data?')) {
const form = document.querySelector('form');
const inputs = form.querySelectorAll('input, select, textarea');
inputs.forEach(input => {
if (input.type !== 'hidden' && input.name !== 'csrfmiddlewaretoken') {
if (input.type === 'checkbox') {
input.checked = false;
} else {
input.value = '';
}
input.classList.remove('is-invalid', 'is-valid');
}
});
// Clear JSON error messages
const errorDivs = form.querySelectorAll('.json-error');
errorDivs.forEach(div => div.remove());
}
}
// Form submission with loading state
document.querySelector('form').addEventListener('submit', function() {
const submitBtn = document.querySelector('button[type="submit"]');
const originalText = submitBtn.innerHTML;
submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin me-2"></i>Processing...';
submitBtn.disabled = true;
// Re-enable if form validation fails
setTimeout(() => {
if (document.querySelector('.is-invalid')) {
submitBtn.innerHTML = originalText;
submitBtn.disabled = false;
}
}, 100);
});
// Real-time validation
document.querySelectorAll('input[required], select[required]').forEach(input => {
input.addEventListener('blur', function() {
if (this.value.trim() === '') {
this.classList.add('is-invalid');
} else {
this.classList.remove('is-invalid');
}
});
input.addEventListener('input', function() {
if (this.classList.contains('is-invalid') && this.value.trim() !== '') {
this.classList.remove('is-invalid');
}
});
});
</script>
<style>
.is-invalid {
border-color: #dc3545;
}
.is-valid {
border-color: #28a745;
}
.invalid-feedback {
display: block;
width: 100%;
margin-top: 0.25rem;
font-size: 0.875em;
color: #dc3545;
}
.text-danger {
color: #dc3545 !important;
}
.accordion-button:not(.collapsed) {
background-color: #e7f1ff;
color: #0d6efd;
}
.form-check-label {
cursor: pointer;
}
code {
background-color: #f8f9fa;
padding: 0.5rem;
border-radius: 0.25rem;
display: block;
font-size: 0.875rem;
color: #495057;
}
@media (max-width: 768px) {
.row.mb-3 .col-md-6,
.row.mb-3 .col-md-4,
.row.mb-3 .col-md-8 {
margin-bottom: 1rem;
}
.d-flex.justify-content-between {
flex-direction: column;
gap: 0.5rem;
}
}
</style>
{% endblock %}