hospital-management/templates/core/system_configuration_form.html
2025-08-12 13:33:25 +03:00

573 lines
31 KiB
HTML

{% extends "base.html" %}
{% load static %}
{% block title %}{% if object %}Edit Configuration - {{ object.key }}{% else %}Add New Configuration{% endif %}{% endblock %}
{% block content %}
<div class="container-fluid">
<!-- Breadcrumb -->
<div class="row">
<div class="col-12">
<div class="page-title-box d-sm-flex align-items-center justify-content-between">
<h4 class="mb-sm-0">{% if object %}Edit Configuration{% else %}Add New Configuration{% endif %}</h4>
<div class="page-title-right">
<ol class="breadcrumb m-0">
<li class="breadcrumb-item"><a href="{% url 'core:dashboard' %}">Dashboard</a></li>
<li class="breadcrumb-item"><a href="{% url 'core:system_configuration' %}">System Configuration</a></li>
<li class="breadcrumb-item active">{% if object %}Edit{% else %}Add{% endif %}</li>
</ol>
</div>
</div>
</div>
</div>
<div class="row">
<!-- Main Form -->
<div class="col-lg-8">
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">
<i class="fas fa-{% if object %}edit{% else %}plus{% endif %} me-2"></i>
Configuration Information
</h5>
</div>
<div class="card-body">
<form method="post" id="configForm">
{% csrf_token %}
<!-- Basic Information -->
<div class="row mb-4">
<div class="col-md-6">
<div class="form-floating mb-3">
{{ form.key }}
<label for="{{ form.key.id_for_label }}">Key *</label>
{% if form.key.errors %}
<div class="invalid-feedback d-block">{{ form.key.errors.0 }}</div>
{% endif %}
<div class="form-text">Unique identifier for this configuration</div>
</div>
</div>
<div class="col-md-6">
<div class="form-floating mb-3">
{{ form.category }}
<label for="{{ form.category.id_for_label }}">Category *</label>
{% if form.category.errors %}
<div class="invalid-feedback d-block">{{ form.category.errors.0 }}</div>
{% endif %}
</div>
</div>
</div>
<div class="row mb-4">
<div class="col-12">
<div class="form-floating mb-3">
{{ form.description }}
<label for="{{ form.description.id_for_label }}">Description</label>
{% if form.description.errors %}
<div class="invalid-feedback d-block">{{ form.description.errors.0 }}</div>
{% endif %}
</div>
</div>
</div>
<!-- Data Type and Value -->
<div class="row mb-4">
<div class="col-md-6">
<div class="form-floating mb-3">
{{ form.data_type }}
<label for="{{ form.data_type.id_for_label }}">Data Type *</label>
{% if form.data_type.errors %}
<div class="invalid-feedback d-block">{{ form.data_type.errors.0 }}</div>
{% endif %}
</div>
</div>
<div class="col-md-6">
<div class="form-check form-switch mt-4">
{{ form.is_active }}
<label class="form-check-label" for="{{ form.is_active.id_for_label }}">
Active Configuration
</label>
{% if form.is_active.errors %}
<div class="invalid-feedback d-block">{{ form.is_active.errors.0 }}</div>
{% endif %}
</div>
</div>
</div>
<!-- Dynamic Value Fields -->
<div class="card mb-4">
<div class="card-header">
<h6 class="card-title mb-0">Configuration Value</h6>
</div>
<div class="card-body">
<!-- Text Value -->
<div id="text-value-field" class="value-field">
<div class="form-floating mb-3">
{{ form.value_text }}
<label for="{{ form.value_text.id_for_label }}">Text Value</label>
{% if form.value_text.errors %}
<div class="invalid-feedback d-block">{{ form.value_text.errors.0 }}</div>
{% endif %}
</div>
</div>
<!-- Integer Value -->
<div id="integer-value-field" class="value-field" style="display: none;">
<div class="form-floating mb-3">
{{ form.value_integer }}
<label for="{{ form.value_integer.id_for_label }}">Integer Value</label>
{% if form.value_integer.errors %}
<div class="invalid-feedback d-block">{{ form.value_integer.errors.0 }}</div>
{% endif %}
</div>
</div>
<!-- Float Value -->
<div id="float-value-field" class="value-field" style="display: none;">
<div class="form-floating mb-3">
{{ form.value_float }}
<label for="{{ form.value_float.id_for_label }}">Float Value</label>
{% if form.value_float.errors %}
<div class="invalid-feedback d-block">{{ form.value_float.errors.0 }}</div>
{% endif %}
</div>
</div>
<!-- Boolean Value -->
<div id="boolean-value-field" class="value-field" style="display: none;">
<div class="form-check form-switch mb-3">
{{ form.value_boolean }}
<label class="form-check-label" for="{{ form.value_boolean.id_for_label }}">
Boolean Value
</label>
{% if form.value_boolean.errors %}
<div class="invalid-feedback d-block">{{ form.value_boolean.errors.0 }}</div>
{% endif %}
</div>
</div>
<!-- Date Value -->
<div id="date-value-field" class="value-field" style="display: none;">
<div class="form-floating mb-3">
{{ form.value_date }}
<label for="{{ form.value_date.id_for_label }}">Date Value</label>
{% if form.value_date.errors %}
<div class="invalid-feedback d-block">{{ form.value_date.errors.0 }}</div>
{% endif %}
</div>
</div>
<!-- DateTime Value -->
<div id="datetime-value-field" class="value-field" style="display: none;">
<div class="form-floating mb-3">
{{ form.value_datetime }}
<label for="{{ form.value_datetime.id_for_label }}">DateTime Value</label>
{% if form.value_datetime.errors %}
<div class="invalid-feedback d-block">{{ form.value_datetime.errors.0 }}</div>
{% endif %}
</div>
</div>
<!-- JSON Value -->
<div id="json-value-field" class="value-field" style="display: none;">
<div class="mb-3">
<label for="{{ form.value_json.id_for_label }}" class="form-label">JSON Value</label>
{{ form.value_json }}
{% if form.value_json.errors %}
<div class="invalid-feedback d-block">{{ form.value_json.errors.0 }}</div>
{% endif %}
<div class="form-text">Enter valid JSON format</div>
</div>
<div class="mb-3">
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="validateJson()">
<i class="fas fa-check me-1"></i>
Validate JSON
</button>
<button type="button" class="btn btn-sm btn-outline-secondary ms-2" onclick="formatJson()">
<i class="fas fa-indent me-1"></i>
Format JSON
</button>
</div>
</div>
</div>
</div>
<!-- Advanced Options -->
<div class="card mb-4">
<div class="card-header d-flex justify-content-between align-items-center">
<h6 class="card-title mb-0">Advanced Options</h6>
<button class="btn btn-sm btn-link" type="button" data-bs-toggle="collapse" data-bs-target="#advancedOptions">
<i class="fas fa-chevron-down"></i>
</button>
</div>
<div class="collapse" id="advancedOptions">
<div class="card-body">
<div class="row">
<div class="col-md-6">
<div class="form-floating mb-3">
{{ form.validation_regex }}
<label for="{{ form.validation_regex.id_for_label }}">Validation Regex</label>
{% if form.validation_regex.errors %}
<div class="invalid-feedback d-block">{{ form.validation_regex.errors.0 }}</div>
{% endif %}
<div class="form-text">Regular expression for validation</div>
</div>
</div>
<div class="col-md-6">
<div class="form-floating mb-3">
{{ form.validation_message }}
<label for="{{ form.validation_message.id_for_label }}">Validation Message</label>
{% if form.validation_message.errors %}
<div class="invalid-feedback d-block">{{ form.validation_message.errors.0 }}</div>
{% endif %}
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-floating mb-3">
{{ form.min_value }}
<label for="{{ form.min_value.id_for_label }}">Minimum Value</label>
{% if form.min_value.errors %}
<div class="invalid-feedback d-block">{{ form.min_value.errors.0 }}</div>
{% endif %}
</div>
</div>
<div class="col-md-6">
<div class="form-floating mb-3">
{{ form.max_value }}
<label for="{{ form.max_value.id_for_label }}">Maximum Value</label>
{% if form.max_value.errors %}
<div class="invalid-feedback d-block">{{ form.max_value.errors.0 }}</div>
{% endif %}
</div>
</div>
</div>
<div class="row">
<div class="col-12">
<div class="form-floating mb-3">
{{ form.allowed_values }}
<label for="{{ form.allowed_values.id_for_label }}">Allowed Values</label>
{% if form.allowed_values.errors %}
<div class="invalid-feedback d-block">{{ form.allowed_values.errors.0 }}</div>
{% endif %}
<div class="form-text">Comma-separated list of allowed values</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Form Actions -->
<div class="d-flex justify-content-between">
<a href="{% url 'core:system_configuration' %}" class="btn btn-outline-secondary">
<i class="fas fa-arrow-left me-1"></i>
Cancel
</a>
<div>
<button type="button" class="btn btn-outline-primary me-2" onclick="testConfiguration()">
<i class="fas fa-flask me-1"></i>
Test Configuration
</button>
<button type="submit" class="btn btn-primary">
<i class="fas fa-save me-1"></i>
{% if object %}Update Configuration{% else %}Create Configuration{% endif %}
</button>
</div>
</div>
</form>
</div>
</div>
</div>
<!-- Help Sidebar -->
<div class="col-lg-4">
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">
<i class="fas fa-question-circle me-2"></i>
Help & Guidelines
</h5>
</div>
<div class="card-body">
<div class="accordion" id="helpAccordion">
<!-- Key Naming -->
<div class="accordion-item">
<h2 class="accordion-header" id="keyNamingHelp">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#keyNamingContent">
Key Naming Conventions
</button>
</h2>
<div id="keyNamingContent" class="accordion-collapse collapse" data-bs-parent="#helpAccordion">
<div class="accordion-body">
<ul class="list-unstyled">
<li><i class="fas fa-check-circle text-success me-2"></i> Use uppercase with underscores</li>
<li><i class="fas fa-check-circle text-success me-2"></i> Group related settings with prefixes</li>
<li><i class="fas fa-check-circle text-success me-2"></i> Be descriptive but concise</li>
<li><i class="fas fa-times-circle text-danger me-2"></i> Avoid spaces or special characters</li>
</ul>
<div class="alert alert-info alert-sm">
<small><strong>Examples:</strong><br>
EMAIL_SMTP_HOST<br>
PATIENT_PORTAL_ENABLED<br>
MAX_APPOINTMENT_DAYS</small>
</div>
</div>
</div>
</div>
<!-- Data Types -->
<div class="accordion-item">
<h2 class="accordion-header" id="dataTypesHelp">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#dataTypesContent">
Data Types Guide
</button>
</h2>
<div id="dataTypesContent" class="accordion-collapse collapse" data-bs-parent="#helpAccordion">
<div class="accordion-body">
<ul class="list-unstyled">
<li><strong>TEXT:</strong> For strings, names, descriptions</li>
<li><strong>INTEGER:</strong> For whole numbers</li>
<li><strong>FLOAT:</strong> For decimal numbers</li>
<li><strong>BOOLEAN:</strong> For true/false settings</li>
<li><strong>DATE:</strong> For date values</li>
<li><strong>DATETIME:</strong> For date and time values</li>
<li><strong>JSON:</strong> For structured data</li>
</ul>
<div class="alert alert-warning alert-sm">
<small><strong>Note:</strong> Changing data type after creation may cause data loss</small>
</div>
</div>
</div>
</div>
<!-- Categories -->
<div class="accordion-item">
<h2 class="accordion-header" id="categoriesHelp">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#categoriesContent">
Common Categories
</button>
</h2>
<div id="categoriesContent" class="accordion-collapse collapse" data-bs-parent="#helpAccordion">
<div class="accordion-body">
<ul class="list-unstyled">
<li><strong>SYSTEM:</strong> Core system settings</li>
<li><strong>EMAIL:</strong> Email configuration</li>
<li><strong>SECURITY:</strong> Security settings</li>
<li><strong>UI:</strong> User interface settings</li>
<li><strong>BILLING:</strong> Billing and payment settings</li>
<li><strong>APPOINTMENTS:</strong> Appointment settings</li>
<li><strong>NOTIFICATIONS:</strong> Notification settings</li>
<li><strong>INTEGRATION:</strong> External integration settings</li>
</ul>
</div>
</div>
</div>
<!-- JSON Format -->
<div class="accordion-item">
<h2 class="accordion-header" id="jsonHelp">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#jsonContent">
JSON Format Help
</button>
</h2>
<div id="jsonContent" class="accordion-collapse collapse" data-bs-parent="#helpAccordion">
<div class="accordion-body">
<p class="mb-2">Valid JSON requires:</p>
<ul class="list-unstyled">
<li><i class="fas fa-check-circle text-success me-2"></i> Double quotes for keys</li>
<li><i class="fas fa-check-circle text-success me-2"></i> Commas between items</li>
<li><i class="fas fa-check-circle text-success me-2"></i> Proper nesting of objects and arrays</li>
</ul>
<div class="alert alert-info alert-sm">
<small><strong>Example:</strong><br>
{<br>
&nbsp;&nbsp;"name": "value",<br>
&nbsp;&nbsp;"items": [1, 2, 3],<br>
&nbsp;&nbsp;"nested": {<br>
&nbsp;&nbsp;&nbsp;&nbsp;"key": "value"<br>
&nbsp;&nbsp;}<br>
}</small>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Test Results -->
<div class="card" id="testResultsCard" style="display: none;">
<div class="card-header">
<h5 class="card-title mb-0">
<i class="fas fa-flask me-2"></i>
Test Results
</h5>
</div>
<div class="card-body" id="testResults">
<!-- Test results will be displayed here -->
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block js %}
<script>
document.addEventListener('DOMContentLoaded', function() {
const dataTypeSelect = document.getElementById('{{ form.data_type.id_for_label }}');
// Show/hide value fields based on data type
function toggleValueFields() {
const dataType = dataTypeSelect.value;
const valueFields = document.querySelectorAll('.value-field');
valueFields.forEach(field => {
field.style.display = 'none';
});
if (dataType) {
const fieldMap = {
'TEXT': 'text-value-field',
'INTEGER': 'integer-value-field',
'FLOAT': 'float-value-field',
'BOOLEAN': 'boolean-value-field',
'DATE': 'date-value-field',
'DATETIME': 'datetime-value-field',
'JSON': 'json-value-field'
};
const fieldId = fieldMap[dataType];
if (fieldId) {
document.getElementById(fieldId).style.display = 'block';
}
}
}
// Initialize value fields display
toggleValueFields();
// Listen for data type changes
dataTypeSelect.addEventListener('change', toggleValueFields);
});
function validateJson() {
const jsonField = document.getElementById('{{ form.value_json.id_for_label }}');
const jsonValue = jsonField.value.trim();
if (!jsonValue) {
alert('Please enter JSON content to validate');
return;
}
try {
const parsedJson = JSON.parse(jsonValue);
alert('JSON is valid!');
} catch (error) {
alert('Invalid JSON: ' + error.message);
}
}
function formatJson() {
const jsonField = document.getElementById('{{ form.value_json.id_for_label }}');
const jsonValue = jsonField.value.trim();
if (!jsonValue) {
alert('Please enter JSON content to format');
return;
}
try {
const parsedJson = JSON.parse(jsonValue);
jsonField.value = JSON.stringify(parsedJson, null, 2);
} catch (error) {
alert('Cannot format invalid JSON: ' + error.message);
}
}
function testConfiguration() {
const form = document.getElementById('configForm');
const formData = new FormData(form);
// Show loading state
const testButton = event.target;
const originalText = testButton.innerHTML;
testButton.innerHTML = '<i class="fas fa-spinner fa-spin me-1"></i>Testing...';
testButton.disabled = true;
fetch('{% if object %}{% url "core:system_configuration_update" object.pk %}{% else %}{% url "core:system_configuration_create" %}{% endif %}test/', {
method: 'POST',
headers: {
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value,
},
body: formData
})
.then(response => response.json())
.then(data => {
// Show test results
const testResultsCard = document.getElementById('testResultsCard');
const testResults = document.getElementById('testResults');
if (data.success) {
testResults.innerHTML = `
<div class="alert alert-success" role="alert">
<div class="d-flex">
<div class="flex-shrink-0">
<i class="fas fa-check-circle fa-lg"></i>
</div>
<div class="flex-grow-1 ms-3">
<h6 class="alert-heading">Configuration Valid!</h6>
<p class="mb-0">${data.message}</p>
</div>
</div>
</div>
`;
} else {
testResults.innerHTML = `
<div class="alert alert-danger" role="alert">
<div class="d-flex">
<div class="flex-shrink-0">
<i class="fas fa-times-circle fa-lg"></i>
</div>
<div class="flex-grow-1 ms-3">
<h6 class="alert-heading">Configuration Invalid</h6>
<p class="mb-0">${data.error}</p>
</div>
</div>
</div>
`;
}
testResultsCard.style.display = 'block';
testResultsCard.scrollIntoView({ behavior: 'smooth' });
})
.catch(error => {
console.error('Test error:', error);
const testResults = document.getElementById('testResults');
testResults.innerHTML = `
<div class="alert alert-danger" role="alert">
<div class="d-flex">
<div class="flex-shrink-0">
<i class="fas fa-times-circle fa-lg"></i>
</div>
<div class="flex-grow-1 ms-3">
<h6 class="alert-heading">Test Error</h6>
<p class="mb-0">An error occurred while testing the configuration.</p>
</div>
</div>
</div>
`;
document.getElementById('testResultsCard').style.display = 'block';
})
.finally(() => {
// Restore button state
testButton.innerHTML = originalText;
testButton.disabled = false;
});
}
</script>
{% endblock %}