726 lines
32 KiB
HTML
726 lines
32 KiB
HTML
{% extends "base.html" %}
|
|
{% load static %}
|
|
|
|
{% block title %}External Systems - {{ 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">
|
|
<i class="fas fa-network-wired me-2"></i>External Systems
|
|
</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 active">External Systems</li>
|
|
</ol>
|
|
</nav>
|
|
</div>
|
|
<div class="btn-group">
|
|
<button type="button" class="btn btn-outline-secondary" onclick="refreshSystems()">
|
|
<i class="fas fa-sync-alt me-2"></i>Refresh
|
|
</button>
|
|
<div class="btn-group">
|
|
<button type="button" class="btn btn-outline-secondary dropdown-toggle" data-bs-toggle="dropdown">
|
|
<i class="fas fa-download me-2"></i>Export
|
|
</button>
|
|
<ul class="dropdown-menu">
|
|
<li><a class="dropdown-item" href="#" onclick="exportData('csv')">
|
|
<i class="fas fa-file-csv me-2"></i>Export as CSV
|
|
</a></li>
|
|
<li><a class="dropdown-item" href="#" onclick="exportData('json')">
|
|
<i class="fas fa-file-code me-2"></i>Export as JSON
|
|
</a></li>
|
|
</ul>
|
|
</div>
|
|
<a href="{% url 'integration:external_system_create' %}" class="btn btn-primary">
|
|
<i class="fas fa-plus me-2"></i>Add System
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Statistics Cards -->
|
|
<div class="row mb-4">
|
|
<div class="col-lg-3 col-md-6 mb-3">
|
|
<div class="card border-0 shadow-sm h-100">
|
|
<div class="card-body">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<div>
|
|
<h6 class="card-title text-muted mb-1">Total Systems</h6>
|
|
<h3 class="mb-0 text-primary">{{ total_systems }}</h3>
|
|
<small class="text-muted">Configured</small>
|
|
</div>
|
|
<div class="bg-primary bg-gradient rounded-circle d-flex align-items-center justify-content-center" style="width: 50px; height: 50px;">
|
|
<i class="fas fa-network-wired fa-lg text-white"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-lg-3 col-md-6 mb-3">
|
|
<div class="card border-0 shadow-sm h-100">
|
|
<div class="card-body">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<div>
|
|
<h6 class="card-title text-muted mb-1">Active Systems</h6>
|
|
<h3 class="mb-0 text-success">{{ active_systems }}</h3>
|
|
<small class="text-muted">Online</small>
|
|
</div>
|
|
<div class="bg-success bg-gradient rounded-circle d-flex align-items-center justify-content-center" style="width: 50px; height: 50px;">
|
|
<i class="fas fa-check-circle fa-lg text-white"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-lg-3 col-md-6 mb-3">
|
|
<div class="card border-0 shadow-sm h-100">
|
|
<div class="card-body">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<div>
|
|
<h6 class="card-title text-muted mb-1">Healthy Systems</h6>
|
|
<h3 class="mb-0 text-info">{{ healthy_systems }}</h3>
|
|
<small class="text-muted">Responding</small>
|
|
</div>
|
|
<div class="bg-info bg-gradient rounded-circle d-flex align-items-center justify-content-center" style="width: 50px; height: 50px;">
|
|
<i class="fas fa-heartbeat fa-lg text-white"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-lg-3 col-md-6 mb-3">
|
|
<div class="card border-0 shadow-sm h-100">
|
|
<div class="card-body">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<div>
|
|
<h6 class="card-title text-muted mb-1">Failed Systems</h6>
|
|
<h3 class="mb-0 text-danger">{{ failed_systems }}</h3>
|
|
<small class="text-muted">Need attention</small>
|
|
</div>
|
|
<div class="bg-danger bg-gradient rounded-circle d-flex align-items-center justify-content-center" style="width: 50px; height: 50px;">
|
|
<i class="fas fa-exclamation-triangle fa-lg text-white"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Filters and Search -->
|
|
<div class="card mb-4">
|
|
<div class="card-header">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-filter me-2"></i>Filters and Search
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<form method="get" id="filterForm">
|
|
<div class="row">
|
|
<div class="col-md-3">
|
|
<label for="system_type" class="form-label">System Type</label>
|
|
<select name="system_type" id="system_type" class="form-select">
|
|
<option value="">All Types</option>
|
|
{% for value, label in system_types %}
|
|
<option value="{{ value }}" {% if request.GET.system_type == value %}selected{% endif %}>{{ label }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<label for="status" class="form-label">Status</label>
|
|
<select name="status" id="status" class="form-select">
|
|
<option value="">All Status</option>
|
|
<option value="active" {% if request.GET.status == 'active' %}selected{% endif %}>Active</option>
|
|
<option value="inactive" {% if request.GET.status == 'inactive' %}selected{% endif %}>Inactive</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<label for="health_status" class="form-label">Health</label>
|
|
<select name="health_status" id="health_status" class="form-select">
|
|
<option value="">All Health</option>
|
|
<option value="healthy" {% if request.GET.health_status == 'healthy' %}selected{% endif %}>Healthy</option>
|
|
<option value="unhealthy" {% if request.GET.health_status == 'unhealthy' %}selected{% endif %}>Unhealthy</option>
|
|
<option value="unknown" {% if request.GET.health_status == 'unknown' %}selected{% endif %}>Unknown</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label for="search" class="form-label">Search</label>
|
|
<input type="text" name="search" id="search" class="form-control"
|
|
placeholder="System name, vendor, description..." value="{{ request.GET.search }}">
|
|
</div>
|
|
<div class="col-md-1 d-flex align-items-end">
|
|
<button type="submit" class="btn btn-primary w-100">
|
|
<i class="fas fa-search"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row mt-3">
|
|
<div class="col-12">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<div>
|
|
{% if request.GET %}
|
|
<a href="{% url 'integration:external_system_list' %}" class="btn btn-outline-secondary btn-sm">
|
|
<i class="fas fa-times me-1"></i>Clear Filters
|
|
</a>
|
|
{% endif %}
|
|
</div>
|
|
<div>
|
|
<span class="text-muted">{{ systems.count }} system{{ systems.count|pluralize }} found</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Systems Table -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-list me-2"></i>External Systems
|
|
</h5>
|
|
<div class="btn-group btn-group-sm">
|
|
<button type="button" class="btn btn-outline-secondary" onclick="selectAll()">
|
|
<i class="fas fa-check-square me-1"></i>Select All
|
|
</button>
|
|
<button type="button" class="btn btn-outline-secondary" onclick="clearSelection()">
|
|
<i class="fas fa-square me-1"></i>Clear
|
|
</button>
|
|
<div class="btn-group">
|
|
<button type="button" class="btn btn-outline-primary dropdown-toggle" data-bs-toggle="dropdown" id="bulkActions" disabled>
|
|
<i class="fas fa-cogs me-1"></i>Bulk Actions
|
|
</button>
|
|
<ul class="dropdown-menu">
|
|
<li><a class="dropdown-item" href="#" onclick="bulkAction('test')">
|
|
<i class="fas fa-plug me-2"></i>Test Connections
|
|
</a></li>
|
|
<li><a class="dropdown-item" href="#" onclick="bulkAction('activate')">
|
|
<i class="fas fa-play me-2"></i>Activate Selected
|
|
</a></li>
|
|
<li><a class="dropdown-item" href="#" onclick="bulkAction('deactivate')">
|
|
<i class="fas fa-pause me-2"></i>Deactivate Selected
|
|
</a></li>
|
|
<li><hr class="dropdown-divider"></li>
|
|
<li><a class="dropdown-item" href="#" onclick="bulkAction('export')">
|
|
<i class="fas fa-download me-2"></i>Export Selected
|
|
</a></li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="card-body p-0">
|
|
<div class="table-responsive">
|
|
<table class="table table-hover mb-0">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th width="40">
|
|
<input type="checkbox" class="form-check-input" id="selectAllCheckbox" onchange="toggleSelectAll()">
|
|
</th>
|
|
<th>System</th>
|
|
<th>Type</th>
|
|
<th>Vendor</th>
|
|
<th>Connection</th>
|
|
<th>Health Status</th>
|
|
<th>Last Check</th>
|
|
<th>Status</th>
|
|
<th width="120">Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for system in systems %}
|
|
<tr>
|
|
<td>
|
|
<input type="checkbox" class="form-check-input system-checkbox" value="{{ system.system_id }}" onchange="updateBulkActions()">
|
|
</td>
|
|
<td>
|
|
<div class="d-flex align-items-center">
|
|
<div class="bg-primary bg-gradient rounded-circle d-flex align-items-center justify-content-center me-2" style="width: 32px; height: 32px;">
|
|
<i class="fas fa-{% if system.system_type == 'ehr' %}file-medical{% elif system.system_type == 'lis' %}flask{% elif system.system_type == 'ris' %}x-ray{% elif system.system_type == 'pacs' %}images{% elif system.system_type == 'pharmacy' %}pills{% elif system.system_type == 'billing' %}receipt{% else %}server{% endif %} text-white small"></i>
|
|
</div>
|
|
<div>
|
|
<div class="fw-semibold">{{ system.name }}</div>
|
|
<small class="text-muted">{{ system.description|truncatechars:40 }}</small>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<span class="badge bg-secondary">{{ system.get_system_type_display }}</span>
|
|
</td>
|
|
<td>
|
|
{% if system.vendor %}
|
|
<div>{{ system.vendor }}</div>
|
|
{% if system.version %}
|
|
<small class="text-muted">v{{ system.version }}</small>
|
|
{% endif %}
|
|
{% else %}
|
|
<span class="text-muted">Not specified</span>
|
|
{% endif %}
|
|
</td>
|
|
<td>
|
|
{% if system.base_url %}
|
|
<div class="small">{{ system.base_url|truncatechars:30 }}</div>
|
|
{% elif system.host %}
|
|
<div class="small">{{ system.host }}{% if system.port %}:{{ system.port }}{% endif %}</div>
|
|
{% else %}
|
|
<span class="text-muted">Not configured</span>
|
|
{% endif %}
|
|
<small class="text-muted">{{ system.get_authentication_type_display }}</small>
|
|
</td>
|
|
<td>
|
|
{% if system.last_health_check_status == 'HEALTHY' %}
|
|
<span class="badge bg-success">
|
|
<i class="fas fa-check-circle me-1"></i>Healthy
|
|
</span>
|
|
{% elif system.last_health_check_status == 'UNHEALTHY' %}
|
|
<span class="badge bg-danger">
|
|
<i class="fas fa-times-circle me-1"></i>Unhealthy
|
|
</span>
|
|
{% else %}
|
|
<span class="badge bg-secondary">
|
|
<i class="fas fa-question-circle me-1"></i>Unknown
|
|
</span>
|
|
{% endif %}
|
|
</td>
|
|
<td>
|
|
{% if system.last_health_check %}
|
|
<div>{{ system.last_health_check|date:"M d, H:i" }}</div>
|
|
<small class="text-muted">{{ system.last_health_check|timesince }} ago</small>
|
|
{% else %}
|
|
<span class="text-muted">Never</span>
|
|
{% endif %}
|
|
</td>
|
|
<td>
|
|
<span class="badge bg-{% if system.is_active %}success{% else %}secondary{% endif %}">
|
|
{% if system.is_active %}Active{% else %}Inactive{% endif %}
|
|
</span>
|
|
</td>
|
|
<td>
|
|
<div class="btn-group btn-group-sm">
|
|
<a href="{% url 'integration:external_system_detail' system.system_id %}"
|
|
class="btn btn-outline-primary btn-sm" title="View Details">
|
|
<i class="fas fa-eye"></i>
|
|
</a>
|
|
<button type="button" class="btn btn-outline-secondary btn-sm dropdown-toggle dropdown-toggle-split"
|
|
data-bs-toggle="dropdown">
|
|
<span class="visually-hidden">Toggle Dropdown</span>
|
|
</button>
|
|
<ul class="dropdown-menu">
|
|
<li><a class="dropdown-item" href="{% url 'integration:external_system_update' system.system_id %}">
|
|
<i class="fas fa-edit me-2"></i>Edit
|
|
</a></li>
|
|
<li><a class="dropdown-item" href="#" onclick="testConnection('{{ system.system_id }}')">
|
|
<i class="fas fa-plug me-2"></i>Test Connection
|
|
</a></li>
|
|
<li><hr class="dropdown-divider"></li>
|
|
{% if system.is_active %}
|
|
<li><a class="dropdown-item" href="#" onclick="toggleSystemStatus('{{ system.system_id }}', false)">
|
|
<i class="fas fa-pause me-2"></i>Deactivate
|
|
</a></li>
|
|
{% else %}
|
|
<li><a class="dropdown-item" href="#" onclick="toggleSystemStatus('{{ system.system_id }}', true)">
|
|
<i class="fas fa-play me-2"></i>Activate
|
|
</a></li>
|
|
{% endif %}
|
|
<li><hr class="dropdown-divider"></li>
|
|
<li><a class="dropdown-item text-danger" href="{% url 'integration:external_system_delete' system.system_id %}">
|
|
<i class="fas fa-trash me-2"></i>Delete
|
|
</a></li>
|
|
</ul>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
{% empty %}
|
|
<tr>
|
|
<td colspan="9" class="text-center py-4">
|
|
<i class="fas fa-network-wired fa-3x text-muted mb-3"></i>
|
|
<h6 class="text-muted">No external systems found</h6>
|
|
<p class="text-muted">
|
|
{% if request.GET %}
|
|
Try adjusting your search criteria or
|
|
<a href="{% url 'integration:external_system_list' %}">clear filters</a>
|
|
{% else %}
|
|
External systems will appear here once configured
|
|
{% endif %}
|
|
</p>
|
|
<a href="{% url 'integration:external_system_create' %}" class="btn btn-primary">
|
|
<i class="fas fa-plus me-2"></i>Add First System
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Pagination -->
|
|
{% if is_paginated %}
|
|
<div class="card-footer">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<div>
|
|
<span class="text-muted">
|
|
Showing {{ page_obj.start_index }} to {{ page_obj.end_index }} of {{ page_obj.paginator.count }} systems
|
|
</span>
|
|
</div>
|
|
<nav aria-label="Systems pagination">
|
|
<ul class="pagination pagination-sm mb-0">
|
|
{% if page_obj.has_previous %}
|
|
<li class="page-item">
|
|
<a class="page-link" href="?{% for key, value in request.GET.items %}{% if key != 'page' %}{{ key }}={{ value }}&{% endif %}{% endfor %}page=1">First</a>
|
|
</li>
|
|
<li class="page-item">
|
|
<a class="page-link" href="?{% for key, value in request.GET.items %}{% if key != 'page' %}{{ key }}={{ value }}&{% endif %}{% endfor %}page={{ page_obj.previous_page_number }}">Previous</a>
|
|
</li>
|
|
{% endif %}
|
|
|
|
{% for num in page_obj.paginator.page_range %}
|
|
{% if page_obj.number == num %}
|
|
<li class="page-item active">
|
|
<span class="page-link">{{ num }}</span>
|
|
</li>
|
|
{% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %}
|
|
<li class="page-item">
|
|
<a class="page-link" href="?{% for key, value in request.GET.items %}{% if key != 'page' %}{{ key }}={{ value }}&{% endif %}{% endfor %}page={{ num }}">{{ num }}</a>
|
|
</li>
|
|
{% endif %}
|
|
{% endfor %}
|
|
|
|
{% if page_obj.has_next %}
|
|
<li class="page-item">
|
|
<a class="page-link" href="?{% for key, value in request.GET.items %}{% if key != 'page' %}{{ key }}={{ value }}&{% endif %}{% endfor %}page={{ page_obj.next_page_number }}">Next</a>
|
|
</li>
|
|
<li class="page-item">
|
|
<a class="page-link" href="?{% for key, value in request.GET.items %}{% if key != 'page' %}{{ key }}={{ value }}&{% endif %}{% endfor %}page={{ page_obj.paginator.num_pages }}">Last</a>
|
|
</li>
|
|
{% endif %}
|
|
</ul>
|
|
</nav>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// External systems list functionality
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// Auto-submit form on filter change
|
|
const filterForm = document.getElementById('filterForm');
|
|
const filterInputs = filterForm.querySelectorAll('select, input[type="text"]');
|
|
|
|
filterInputs.forEach(input => {
|
|
if (input.type === 'text') {
|
|
// Debounce text input
|
|
let timeout;
|
|
input.addEventListener('input', function() {
|
|
clearTimeout(timeout);
|
|
timeout = setTimeout(() => {
|
|
filterForm.submit();
|
|
}, 500);
|
|
});
|
|
} else {
|
|
input.addEventListener('change', function() {
|
|
filterForm.submit();
|
|
});
|
|
}
|
|
});
|
|
});
|
|
|
|
function refreshSystems() {
|
|
location.reload();
|
|
}
|
|
|
|
function selectAll() {
|
|
const checkboxes = document.querySelectorAll('.system-checkbox');
|
|
checkboxes.forEach(checkbox => {
|
|
checkbox.checked = true;
|
|
});
|
|
updateBulkActions();
|
|
}
|
|
|
|
function clearSelection() {
|
|
const checkboxes = document.querySelectorAll('.system-checkbox');
|
|
checkboxes.forEach(checkbox => {
|
|
checkbox.checked = false;
|
|
});
|
|
document.getElementById('selectAllCheckbox').checked = false;
|
|
updateBulkActions();
|
|
}
|
|
|
|
function toggleSelectAll() {
|
|
const selectAllCheckbox = document.getElementById('selectAllCheckbox');
|
|
const checkboxes = document.querySelectorAll('.system-checkbox');
|
|
|
|
checkboxes.forEach(checkbox => {
|
|
checkbox.checked = selectAllCheckbox.checked;
|
|
});
|
|
updateBulkActions();
|
|
}
|
|
|
|
function updateBulkActions() {
|
|
const checkboxes = document.querySelectorAll('.system-checkbox:checked');
|
|
const bulkActionsBtn = document.getElementById('bulkActions');
|
|
const selectAllCheckbox = document.getElementById('selectAllCheckbox');
|
|
|
|
bulkActionsBtn.disabled = checkboxes.length === 0;
|
|
|
|
// Update select all checkbox state
|
|
const allCheckboxes = document.querySelectorAll('.system-checkbox');
|
|
if (checkboxes.length === 0) {
|
|
selectAllCheckbox.indeterminate = false;
|
|
selectAllCheckbox.checked = false;
|
|
} else if (checkboxes.length === allCheckboxes.length) {
|
|
selectAllCheckbox.indeterminate = false;
|
|
selectAllCheckbox.checked = true;
|
|
} else {
|
|
selectAllCheckbox.indeterminate = true;
|
|
}
|
|
}
|
|
|
|
function bulkAction(action) {
|
|
const checkboxes = document.querySelectorAll('.system-checkbox:checked');
|
|
const systemIds = Array.from(checkboxes).map(cb => cb.value);
|
|
|
|
if (systemIds.length === 0) {
|
|
alert('Please select at least one system.');
|
|
return;
|
|
}
|
|
|
|
switch (action) {
|
|
case 'test':
|
|
if (confirm(`Test connection for ${systemIds.length} selected system(s)?`)) {
|
|
bulkTestConnections(systemIds);
|
|
}
|
|
break;
|
|
case 'activate':
|
|
if (confirm(`Activate ${systemIds.length} selected system(s)?`)) {
|
|
bulkToggleStatus(systemIds, true);
|
|
}
|
|
break;
|
|
case 'deactivate':
|
|
if (confirm(`Deactivate ${systemIds.length} selected system(s)?`)) {
|
|
bulkToggleStatus(systemIds, false);
|
|
}
|
|
break;
|
|
case 'export':
|
|
exportSelectedSystems(systemIds);
|
|
break;
|
|
}
|
|
}
|
|
|
|
function testConnection(systemId) {
|
|
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!');
|
|
} else {
|
|
alert('Connection test failed: ' + data.error);
|
|
}
|
|
location.reload();
|
|
})
|
|
.catch(error => {
|
|
console.error('Error testing connection:', error);
|
|
alert('Error testing connection');
|
|
});
|
|
}
|
|
|
|
function toggleSystemStatus(systemId, activate) {
|
|
const action = activate ? 'activate' : 'deactivate';
|
|
|
|
fetch(`/integration/systems/${systemId}/${action}/`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value,
|
|
'Content-Type': 'application/json',
|
|
},
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
location.reload();
|
|
} else {
|
|
alert('Error: ' + data.error);
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Error toggling system status:', error);
|
|
alert('Error updating system status');
|
|
});
|
|
}
|
|
|
|
function bulkTestConnections(systemIds) {
|
|
// Show loading state
|
|
const bulkActionsBtn = document.getElementById('bulkActions');
|
|
const originalText = bulkActionsBtn.innerHTML;
|
|
bulkActionsBtn.innerHTML = '<i class="fas fa-spinner fa-spin me-1"></i>Testing...';
|
|
bulkActionsBtn.disabled = true;
|
|
|
|
fetch('/integration/systems/bulk-test/', {
|
|
method: 'POST',
|
|
headers: {
|
|
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value,
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({ system_ids: systemIds })
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
alert(`Connection tests completed. ${data.successful} successful, ${data.failed} failed.`);
|
|
location.reload();
|
|
})
|
|
.catch(error => {
|
|
console.error('Error testing connections:', error);
|
|
alert('Error testing connections');
|
|
bulkActionsBtn.innerHTML = originalText;
|
|
bulkActionsBtn.disabled = false;
|
|
});
|
|
}
|
|
|
|
function bulkToggleStatus(systemIds, activate) {
|
|
const action = activate ? 'activate' : 'deactivate';
|
|
|
|
fetch(`/integration/systems/bulk-${action}/`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value,
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({ system_ids: systemIds })
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
location.reload();
|
|
} else {
|
|
alert('Error: ' + data.error);
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Error updating systems:', error);
|
|
alert('Error updating systems');
|
|
});
|
|
}
|
|
|
|
function exportData(format) {
|
|
const form = document.createElement('form');
|
|
form.method = 'POST';
|
|
form.action = '{% url "integration:export_systems" %}';
|
|
|
|
const csrfToken = document.createElement('input');
|
|
csrfToken.type = 'hidden';
|
|
csrfToken.name = 'csrfmiddlewaretoken';
|
|
csrfToken.value = document.querySelector('[name=csrfmiddlewaretoken]').value;
|
|
|
|
const formatInput = document.createElement('input');
|
|
formatInput.type = 'hidden';
|
|
formatInput.name = 'format';
|
|
formatInput.value = format;
|
|
|
|
// Add current filters
|
|
const filterForm = document.getElementById('filterForm');
|
|
const formData = new FormData(filterForm);
|
|
for (let [key, value] of formData.entries()) {
|
|
if (value) {
|
|
const input = document.createElement('input');
|
|
input.type = 'hidden';
|
|
input.name = key;
|
|
input.value = value;
|
|
form.appendChild(input);
|
|
}
|
|
}
|
|
|
|
form.appendChild(csrfToken);
|
|
form.appendChild(formatInput);
|
|
document.body.appendChild(form);
|
|
form.submit();
|
|
document.body.removeChild(form);
|
|
}
|
|
|
|
function exportSelectedSystems(systemIds) {
|
|
const form = document.createElement('form');
|
|
form.method = 'POST';
|
|
form.action = '{% url "integration:export_systems" %}';
|
|
|
|
const csrfToken = document.createElement('input');
|
|
csrfToken.type = 'hidden';
|
|
csrfToken.name = 'csrfmiddlewaretoken';
|
|
csrfToken.value = document.querySelector('[name=csrfmiddlewaretoken]').value;
|
|
|
|
systemIds.forEach(id => {
|
|
const input = document.createElement('input');
|
|
input.type = 'hidden';
|
|
input.name = 'system_ids';
|
|
input.value = id;
|
|
form.appendChild(input);
|
|
});
|
|
|
|
form.appendChild(csrfToken);
|
|
document.body.appendChild(form);
|
|
form.submit();
|
|
document.body.removeChild(form);
|
|
}
|
|
</script>
|
|
|
|
<style>
|
|
.bg-gradient {
|
|
background-image: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));
|
|
}
|
|
|
|
.table th {
|
|
border-top: none;
|
|
font-weight: 600;
|
|
color: #6c757d;
|
|
font-size: 0.875rem;
|
|
}
|
|
|
|
.table td {
|
|
vertical-align: middle;
|
|
font-size: 0.875rem;
|
|
}
|
|
|
|
.badge {
|
|
font-size: 0.75em;
|
|
}
|
|
|
|
.form-check-input:indeterminate {
|
|
background-color: #0d6efd;
|
|
border-color: #0d6efd;
|
|
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e");
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.d-flex.justify-content-between {
|
|
flex-direction: column;
|
|
gap: 1rem;
|
|
}
|
|
|
|
.btn-group {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.table-responsive {
|
|
font-size: 0.8rem;
|
|
}
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|