2025-08-12 13:33:25 +03:00

557 lines
28 KiB
HTML

{% extends "base.html" %}
{% load static %}
{% block title %}{% if object %}Edit {{ object.display_name }}{% else %}Add New Tenant{% 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 %}
Edit Tenant: {{ object.display_name }}
{% else %}
Add New Tenant
{% endif %}
</h1>
<nav aria-label="breadcrumb">
<ol class="breadcrumb mb-0">
<li class="breadcrumb-item"><a href="{% url 'core:dashboard' %}">Dashboard</a></li>
<li class="breadcrumb-item"><a href="{% url 'core:tenant_list' %}">Tenants</a></li>
{% if object %}
<li class="breadcrumb-item"><a href="{% url 'core:tenant_detail' object.pk %}">{{ object.name }}</a></li>
<li class="breadcrumb-item active">Edit</li>
{% else %}
<li class="breadcrumb-item active">Add New</li>
{% endif %}
</ol>
</nav>
</div>
<div class="btn-group">
<a href="{% if object %}{% url 'core:tenant_detail' object.pk %}{% else %}{% url 'core:tenant_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">
Organization 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">Internal name for the organization (used in system)</div>
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="{{ form.display_name.id_for_label }}" class="form-label">
Display Name <span class="text-danger">*</span>
</label>
{{ form.display_name }}
{% if form.display_name.errors %}
<div class="invalid-feedback d-block">
{{ form.display_name.errors.0 }}
</div>
{% endif %}
<div class="form-text">Public-facing name shown to users</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label for="{{ form.organization_type.id_for_label }}" class="form-label">
Organization Type <span class="text-danger">*</span>
</label>
{{ form.organization_type }}
{% if form.organization_type.errors %}
<div class="invalid-feedback d-block">
{{ form.organization_type.errors.0 }}
</div>
{% endif %}
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="{{ form.domain.id_for_label }}" class="form-label">
Domain
</label>
{{ form.domain }}
{% if form.domain.errors %}
<div class="invalid-feedback d-block">
{{ form.domain.errors.0 }}
</div>
{% endif %}
<div class="form-text">Custom domain for this tenant (optional)</div>
</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">Brief description of the organization</div>
</div>
</div>
</div>
<!-- Contact Information -->
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0">
<i class="fas fa-address-book me-2"></i>Contact Information
</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label for="{{ form.contact_email.id_for_label }}" class="form-label">
Primary Email
</label>
{{ form.contact_email }}
{% if form.contact_email.errors %}
<div class="invalid-feedback d-block">
{{ form.contact_email.errors.0 }}
</div>
{% endif %}
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="{{ form.contact_phone.id_for_label }}" class="form-label">
Phone Number
</label>
{{ form.contact_phone }}
{% if form.contact_phone.errors %}
<div class="invalid-feedback d-block">
{{ form.contact_phone.errors.0 }}
</div>
{% endif %}
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label for="{{ form.website.id_for_label }}" class="form-label">
Website
</label>
{{ form.website }}
{% if form.website.errors %}
<div class="invalid-feedback d-block">
{{ form.website.errors.0 }}
</div>
{% endif %}
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="{{ form.timezone.id_for_label }}" class="form-label">
Timezone
</label>
{{ form.timezone }}
{% if form.timezone.errors %}
<div class="invalid-feedback d-block">
{{ form.timezone.errors.0 }}
</div>
{% endif %}
</div>
</div>
</div>
<div class="mb-3">
<label for="{{ form.address.id_for_label }}" class="form-label">
Address
</label>
{{ form.address }}
{% if form.address.errors %}
<div class="invalid-feedback d-block">
{{ form.address.errors.0 }}
</div>
{% endif %}
</div>
</div>
</div>
<!-- Configuration Settings -->
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0">
<i class="fas fa-cogs me-2"></i>Configuration Settings
</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label for="{{ form.max_users.id_for_label }}" class="form-label">
Maximum Users
</label>
{{ form.max_users }}
{% if form.max_users.errors %}
<div class="invalid-feedback d-block">
{{ form.max_users.errors.0 }}
</div>
{% endif %}
<div class="form-text">Leave empty for unlimited users</div>
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="{{ form.storage_limit_gb.id_for_label }}" class="form-label">
Storage Limit (GB)
</label>
{{ form.storage_limit_gb }}
{% if form.storage_limit_gb.errors %}
<div class="invalid-feedback d-block">
{{ form.storage_limit_gb.errors.0 }}
</div>
{% endif %}
<div class="form-text">Leave empty for unlimited storage</div>
</div>
</div>
</div>
<div class="mb-3">
<label class="form-label">Features Enabled</label>
<div class="row">
<div class="col-md-4">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="feature_emr" name="features_enabled" value="EMR">
<label class="form-check-label" for="feature_emr">
Electronic Medical Records
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="feature_billing" name="features_enabled" value="BILLING">
<label class="form-check-label" for="feature_billing">
Billing & Payments
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="feature_pharmacy" name="features_enabled" value="PHARMACY">
<label class="form-check-label" for="feature_pharmacy">
Pharmacy Management
</label>
</div>
</div>
<div class="col-md-4">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="feature_lab" name="features_enabled" value="LABORATORY">
<label class="form-check-label" for="feature_lab">
Laboratory Services
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="feature_radiology" name="features_enabled" value="RADIOLOGY">
<label class="form-check-label" for="feature_radiology">
Radiology & Imaging
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="feature_inventory" name="features_enabled" value="INVENTORY">
<label class="form-check-label" for="feature_inventory">
Inventory Management
</label>
</div>
</div>
<div class="col-md-4">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="feature_hr" name="features_enabled" value="HR">
<label class="form-check-label" for="feature_hr">
Human Resources
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="feature_analytics" name="features_enabled" value="ANALYTICS">
<label class="form-check-label" for="feature_analytics">
Analytics & Reporting
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="feature_integration" name="features_enabled" value="INTEGRATION">
<label class="form-check-label" for="feature_integration">
External Integrations
</label>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Sidebar -->
<div class="col-lg-4">
<!-- Status & Actions -->
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0">
<i class="fas fa-toggle-on me-2"></i>Status & Actions
</h5>
</div>
<div class="card-body">
<div class="mb-3">
<div class="form-check form-switch">
{{ form.is_active }}
<label class="form-check-label" for="{{ form.is_active.id_for_label }}">
Active Tenant
</label>
</div>
<div class="form-text">
{% if object %}
Deactivating will prevent users from accessing the system
{% else %}
New tenants are active by default
{% endif %}
</div>
</div>
{% if object %}
<div class="mb-3">
<label class="form-label text-muted">Tenant ID</label>
<div class="input-group">
<input type="text" class="form-control" value="{{ object.tenant_id }}" readonly>
<button class="btn btn-outline-secondary" type="button" onclick="copyToClipboard('{{ object.tenant_id }}')">
<i class="fas fa-copy"></i>
</button>
</div>
</div>
{% endif %}
<div class="d-grid gap-2">
<button type="submit" class="btn btn-primary">
<i class="fas fa-save me-2"></i>
{% if object %}Update Tenant{% else %}Create Tenant{% endif %}
</button>
<a href="{% if object %}{% url 'core:tenant_detail' object.pk %}{% else %}{% url 'core:tenant_list' %}{% endif %}" class="btn btn-outline-secondary">
<i class="fas fa-times me-2"></i>Cancel
</a>
</div>
</div>
</div>
<!-- Help & Guidelines -->
<div class="card">
<div class="card-header">
<h5 class="mb-0">
<i class="fas fa-question-circle me-2"></i>Help & Guidelines
</h5>
</div>
<div class="card-body">
<div class="accordion" id="helpAccordion">
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#helpNaming">
Naming Guidelines
</button>
</h2>
<div id="helpNaming" class="accordion-collapse collapse" data-bs-parent="#helpAccordion">
<div class="accordion-body">
<ul class="mb-0">
<li>Use clear, descriptive names</li>
<li>Organization name should be unique</li>
<li>Display name is shown to users</li>
<li>Avoid special characters in names</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="#helpDomain">
Domain Configuration
</button>
</h2>
<div id="helpDomain" class="accordion-collapse collapse" data-bs-parent="#helpAccordion">
<div class="accordion-body">
<ul class="mb-0">
<li>Domain is optional but recommended</li>
<li>Use format: subdomain.yourdomain.com</li>
<li>Ensure DNS is properly configured</li>
<li>SSL certificate required for custom domains</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="#helpFeatures">
Feature Selection
</button>
</h2>
<div id="helpFeatures" class="accordion-collapse collapse" data-bs-parent="#helpAccordion">
<div class="accordion-body">
<ul class="mb-0">
<li>Select only needed features</li>
<li>Features can be changed later</li>
<li>Some features may require additional setup</li>
<li>Contact support for custom features</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</form>
</div>
<script>
// Form validation and enhancement
document.addEventListener('DOMContentLoaded', function() {
// Add Bootstrap validation classes
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.type === 'checkbox') {
input.classList.add('form-check-input');
} else if (input.tagName === 'SELECT') {
input.classList.add('form-select');
} else {
input.classList.add('form-control');
}
}
// Add validation feedback
if (input.hasAttribute('required')) {
input.addEventListener('invalid', function() {
input.classList.add('is-invalid');
});
input.addEventListener('input', function() {
if (input.validity.valid) {
input.classList.remove('is-invalid');
input.classList.add('is-valid');
}
});
}
});
// Auto-generate display name from organization name
const nameInput = document.getElementById('{{ form.name.id_for_label }}');
const displayNameInput = document.getElementById('{{ form.display_name.id_for_label }}');
if (nameInput && displayNameInput && !displayNameInput.value) {
nameInput.addEventListener('input', function() {
if (!displayNameInput.value || displayNameInput.value === nameInput.dataset.previousValue) {
displayNameInput.value = nameInput.value;
}
nameInput.dataset.previousValue = nameInput.value;
});
}
// Load existing features
{% if object and object.features_enabled %}
const enabledFeatures = {{ object.features_enabled|safe }};
enabledFeatures.forEach(feature => {
const checkbox = document.querySelector(`input[value="${feature}"]`);
if (checkbox) {
checkbox.checked = true;
}
});
{% endif %}
});
function copyToClipboard(text) {
navigator.clipboard.writeText(text).then(function() {
const button = event.target.closest('button');
const originalIcon = button.innerHTML;
button.innerHTML = '<i class="fas fa-check"></i>';
button.classList.add('btn-success');
button.classList.remove('btn-outline-secondary');
setTimeout(() => {
button.innerHTML = originalIcon;
button.classList.remove('btn-success');
button.classList.add('btn-outline-secondary');
}, 2000);
});
}
// 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>Saving...';
submitBtn.disabled = true;
// Re-enable if form validation fails
setTimeout(() => {
if (document.querySelector('.is-invalid')) {
submitBtn.innerHTML = originalText;
submitBtn.disabled = false;
}
}, 100);
});
</script>
<style>
.form-check-input:checked {
background-color: #0d6efd;
border-color: #0d6efd;
}
.accordion-button:not(.collapsed) {
background-color: #e7f1ff;
color: #0d6efd;
}
.is-invalid {
border-color: #dc3545;
}
.is-valid {
border-color: #198754;
}
.invalid-feedback {
display: block;
width: 100%;
margin-top: 0.25rem;
font-size: 0.875em;
color: #dc3545;
}
</style>
{% endblock %}