hospital-management/templates/quality/measurements/measurement_confirm_delete.html
Marwan Alwali 0a037d3d9d update
2025-09-01 11:26:11 +03:00

1069 lines
34 KiB
HTML

{% extends 'base.html' %}
{% load static %}
{% block title %}Delete Measurement - {{ measurement.name }}{% endblock %}
{% block extra_css %}
<style>
.delete-header {
background: linear-gradient(135deg, #dc3545 0%, #c82333 100%);
color: white;
border-radius: 0.5rem;
padding: 2rem;
margin-bottom: 2rem;
}
.delete-layout {
display: grid;
grid-template-columns: 2fr 1fr;
gap: 2rem;
margin-bottom: 2rem;
}
.delete-main {
display: flex;
flex-direction: column;
gap: 2rem;
}
.delete-sidebar {
display: flex;
flex-direction: column;
gap: 1.5rem;
}
.section-card {
background: white;
border: 1px solid #dee2e6;
border-radius: 0.5rem;
overflow: hidden;
}
.section-header {
background: #f8f9fa;
border-bottom: 1px solid #dee2e6;
padding: 1rem 1.5rem;
font-weight: 600;
color: #495057;
display: flex;
align-items: center;
gap: 0.5rem;
}
.section-content {
padding: 1.5rem;
}
.warning-box {
background: #fff3cd;
border: 1px solid #ffeaa7;
border-left: 4px solid #ffc107;
border-radius: 0.375rem;
padding: 1rem;
margin-bottom: 1.5rem;
}
.warning-icon {
color: #856404;
font-size: 1.25rem;
margin-right: 0.5rem;
}
.warning-title {
font-weight: 600;
color: #856404;
margin-bottom: 0.5rem;
}
.warning-text {
color: #856404;
margin: 0;
}
.danger-box {
background: #f8d7da;
border: 1px solid #f5c6cb;
border-left: 4px solid #dc3545;
border-radius: 0.375rem;
padding: 1rem;
margin-bottom: 1.5rem;
}
.danger-icon {
color: #721c24;
font-size: 1.25rem;
margin-right: 0.5rem;
}
.danger-title {
font-weight: 600;
color: #721c24;
margin-bottom: 0.5rem;
}
.danger-text {
color: #721c24;
margin: 0;
}
.measurement-info {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1rem;
margin-bottom: 1.5rem;
}
.info-item {
display: flex;
flex-direction: column;
gap: 0.25rem;
}
.info-label {
font-size: 0.75rem;
color: #6c757d;
font-weight: 600;
text-transform: uppercase;
}
.info-value {
font-size: 0.875rem;
color: #495057;
font-weight: 500;
}
.impact-list {
list-style: none;
padding: 0;
margin: 0;
}
.impact-item {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.75rem;
border-bottom: 1px solid #f0f0f0;
transition: background 0.2s;
}
.impact-item:hover {
background: #f8f9fa;
}
.impact-item:last-child {
border-bottom: none;
}
.impact-icon {
width: 32px;
height: 32px;
border-radius: 50%;
background: #dc3545;
color: white;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.875rem;
flex-shrink: 0;
}
.impact-content {
flex: 1;
}
.impact-title {
font-weight: 600;
color: #495057;
margin-bottom: 0.25rem;
}
.impact-description {
font-size: 0.875rem;
color: #6c757d;
}
.impact-count {
font-size: 0.875rem;
font-weight: 600;
color: #dc3545;
background: #fff5f5;
padding: 0.25rem 0.5rem;
border-radius: 0.25rem;
}
.data-point-item {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.75rem;
border: 1px solid #dee2e6;
border-radius: 0.375rem;
margin-bottom: 0.75rem;
background: white;
transition: all 0.2s;
}
.data-point-item:hover {
border-color: #dc3545;
background: #fff5f5;
}
.data-point-icon {
width: 32px;
height: 32px;
border-radius: 0.375rem;
background: #dc3545;
color: white;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.875rem;
flex-shrink: 0;
}
.data-point-info {
flex: 1;
}
.data-point-date {
font-weight: 600;
color: #495057;
margin-bottom: 0.25rem;
}
.data-point-value {
font-size: 0.75rem;
color: #6c757d;
}
.data-point-meta {
font-size: 0.75rem;
color: #28a745;
font-weight: 600;
}
.team-member {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.75rem;
border-bottom: 1px solid #f0f0f0;
transition: background 0.2s;
}
.team-member:hover {
background: #f8f9fa;
}
.member-avatar {
width: 40px;
height: 40px;
border-radius: 50%;
background: #dc3545;
color: white;
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
flex-shrink: 0;
}
.member-info {
flex: 1;
}
.member-name {
font-weight: 600;
color: #495057;
margin-bottom: 0.25rem;
}
.member-role {
font-size: 0.75rem;
color: #6c757d;
}
.confirmation-section {
background: #f8f9fa;
border: 2px solid #dee2e6;
border-radius: 0.5rem;
padding: 1.5rem;
margin-bottom: 2rem;
}
.confirmation-title {
font-size: 1.25rem;
font-weight: 600;
color: #495057;
margin-bottom: 1rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
.confirmation-steps {
list-style: none;
padding: 0;
margin: 0;
}
.confirmation-step {
display: flex;
align-items: start;
gap: 0.75rem;
margin-bottom: 1rem;
padding: 0.75rem;
background: white;
border-radius: 0.375rem;
border: 1px solid #dee2e6;
}
.step-number {
width: 24px;
height: 24px;
border-radius: 50%;
background: #dc3545;
color: white;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.75rem;
font-weight: 600;
flex-shrink: 0;
}
.step-content {
flex: 1;
}
.step-title {
font-weight: 600;
color: #495057;
margin-bottom: 0.25rem;
}
.step-description {
font-size: 0.875rem;
color: #6c757d;
}
.confirmation-checkbox {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 1rem;
background: #fff5f5;
border: 2px solid #dc3545;
border-radius: 0.375rem;
margin-bottom: 1.5rem;
}
.confirmation-checkbox input[type="checkbox"] {
width: 20px;
height: 20px;
accent-color: #dc3545;
}
.confirmation-text {
font-weight: 600;
color: #721c24;
}
.delete-actions {
background: white;
border: 1px solid #dee2e6;
border-radius: 0.5rem;
padding: 1.5rem;
display: flex;
justify-content: between;
align-items: center;
gap: 1rem;
}
.btn-group {
display: flex;
gap: 0.5rem;
}
.alternative-actions {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.alternative-text {
font-size: 0.875rem;
color: #6c757d;
margin-bottom: 0.5rem;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 1rem;
}
.stat-item {
text-align: center;
padding: 1rem;
background: #f8f9fa;
border-radius: 0.375rem;
}
.stat-number {
font-size: 1.5rem;
font-weight: bold;
color: #dc3545;
margin-bottom: 0.25rem;
}
.stat-label {
font-size: 0.75rem;
color: #6c757d;
font-weight: 600;
text-transform: uppercase;
}
.chart-preview {
height: 150px;
background: #f8f9fa;
border-radius: 0.375rem;
display: flex;
align-items: center;
justify-content: center;
color: #6c757d;
font-size: 0.875rem;
margin-bottom: 1rem;
}
@media (max-width: 1200px) {
.delete-layout {
grid-template-columns: 1fr;
gap: 1.5rem;
}
.delete-sidebar {
order: -1;
}
}
@media (max-width: 768px) {
.delete-header {
padding: 1.5rem;
}
.measurement-info {
grid-template-columns: 1fr;
}
.stats-grid {
grid-template-columns: 1fr;
}
.delete-actions {
flex-direction: column;
align-items: stretch;
}
.btn-group {
justify-content: center;
}
}
@media print {
.delete-sidebar, .delete-actions {
display: none !important;
}
.delete-layout {
grid-template-columns: 1fr;
}
.section-header {
background: none;
border-bottom: 2px solid #000;
color: #000;
}
}
</style>
{% endblock %}
{% block content %}
<div id="content" class="app-content">
<!-- Page Header -->
<div class="d-flex align-items-center mb-3">
<div>
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="{% url 'core:dashboard' %}">Dashboard</a></li>
<li class="breadcrumb-item"><a href="{% url 'quality:dashboard' %}">Quality</a></li>
<li class="breadcrumb-item"><a href="{% url 'quality:measurement_list' %}">Measurements</a></li>
<li class="breadcrumb-item"><a href="{% url 'quality:measurement_detail' measurement.id %}">{{ measurement.name|truncatechars:20 }}</a></li>
<li class="breadcrumb-item active">Delete</li>
</ol>
<h1 class="page-header mb-0">
<i class="fas fa-trash me-2"></i>Delete Measurement
</h1>
</div>
<div class="ms-auto">
<a href="{% url 'quality:measurement_detail' measurement.id %}" class="btn btn-outline-secondary">
<i class="fas fa-arrow-left me-1"></i>Back to Measurement
</a>
</div>
</div>
<!-- Delete Header -->
<div class="delete-header">
<div class="row align-items-center">
<div class="col-md-8">
<h2 class="mb-2">
<i class="fas fa-exclamation-triangle me-2"></i>
Delete Measurement: {{ measurement.name }}
</h2>
<p class="mb-0">This action will permanently delete the measurement and all associated data. Please review the impact before proceeding.</p>
</div>
<div class="col-md-4 text-md-end">
<div class="stats-grid">
<div class="stat-item">
<div class="stat-number">{{ measurement.data_points.count|default:0 }}</div>
<div class="stat-label">Data Points</div>
</div>
<div class="stat-item">
<div class="stat-number">{{ measurement.alerts.count|default:0 }}</div>
<div class="stat-label">Active Alerts</div>
</div>
</div>
</div>
</div>
</div>
<div class="delete-layout">
<!-- Main Content -->
<div class="delete-main">
<!-- Warning Messages -->
<div class="danger-box">
<div class="d-flex align-items-start">
<i class="fas fa-exclamation-triangle danger-icon"></i>
<div>
<div class="danger-title">Permanent Deletion Warning</div>
<div class="danger-text">
This action cannot be undone. All measurement data, including historical values,
targets, alerts, and performance tracking will be permanently deleted.
</div>
</div>
</div>
</div>
{% if measurement.status == 'active' %}
<div class="warning-box">
<div class="d-flex align-items-start">
<i class="fas fa-exclamation-circle warning-icon"></i>
<div>
<div class="warning-title">Active Measurement Warning</div>
<div class="warning-text">
This measurement is currently active and being tracked. Consider deactivating
instead of deleting to preserve historical data and trends.
</div>
</div>
</div>
</div>
{% endif %}
<!-- Measurement Information -->
<div class="section-card">
<div class="section-header">
<i class="fas fa-info-circle"></i>
Measurement Information
</div>
<div class="section-content">
<div class="measurement-info">
<div class="info-item">
<div class="info-label">Measurement Name</div>
<div class="info-value">{{ measurement.name }}</div>
</div>
<div class="info-item">
<div class="info-label">Measurement ID</div>
<div class="info-value">{{ measurement.measurement_id|default:measurement.id }}</div>
</div>
<div class="info-item">
<div class="info-label">Category</div>
<div class="info-value">{{ measurement.get_category_display }}</div>
</div>
<div class="info-item">
<div class="info-label">Department</div>
<div class="info-value">{{ measurement.department.name|default:"Not specified" }}</div>
</div>
<div class="info-item">
<div class="info-label">Frequency</div>
<div class="info-value">{{ measurement.get_frequency_display }}</div>
</div>
<div class="info-item">
<div class="info-label">Status</div>
<div class="info-value">{{ measurement.get_status_display }}</div>
</div>
<div class="info-item">
<div class="info-label">Created</div>
<div class="info-value">{{ measurement.created_at|date:"M d, Y" }}</div>
</div>
<div class="info-item">
<div class="info-label">Owner</div>
<div class="info-value">{{ measurement.owner.get_full_name|default:"Not assigned" }}</div>
</div>
<div class="info-item">
<div class="info-label">Current Value</div>
<div class="info-value">{{ measurement.current_value|default:"No data" }} {{ measurement.unit_of_measure|default:"" }}</div>
</div>
<div class="info-item">
<div class="info-label">Target Value</div>
<div class="info-value">{{ measurement.target_value|default:"Not set" }} {{ measurement.unit_of_measure|default:"" }}</div>
</div>
</div>
{% if measurement.description %}
<div class="mt-3">
<div class="info-label">Description</div>
<div class="info-value">{{ measurement.description }}</div>
</div>
{% endif %}
</div>
</div>
<!-- Impact Analysis -->
<div class="section-card">
<div class="section-header">
<i class="fas fa-chart-line"></i>
Deletion Impact Analysis
</div>
<div class="section-content">
<ul class="impact-list">
<li class="impact-item">
<div class="impact-icon">
<i class="fas fa-chart-bar"></i>
</div>
<div class="impact-content">
<div class="impact-title">Historical Data Points</div>
<div class="impact-description">
{{ measurement.data_points.count }} data point{{ measurement.data_points.count|pluralize }} will be permanently deleted
</div>
</div>
<div class="impact-count">{{ measurement.data_points.count }}</div>
</li>
<li class="impact-item">
<div class="impact-icon">
<i class="fas fa-bell"></i>
</div>
<div class="impact-content">
<div class="impact-title">Active Alerts</div>
<div class="impact-description">
All configured alerts and notifications will be removed
</div>
</div>
<div class="impact-count">{{ measurement.alerts.count|default:0 }}</div>
</li>
<li class="impact-item">
<div class="impact-icon">
<i class="fas fa-users"></i>
</div>
<div class="impact-content">
<div class="impact-title">Team Access</div>
<div class="impact-description">
{{ measurement.team_members.count|default:0 }} team member{{ measurement.team_members.count|pluralize }} will lose access to this measurement
</div>
</div>
<div class="impact-count">{{ measurement.team_members.count|default:0 }}</div>
</li>
<li class="impact-item">
<div class="impact-icon">
<i class="fas fa-file-alt"></i>
</div>
<div class="impact-content">
<div class="impact-title">Reports & Analytics</div>
<div class="impact-description">
Historical reports and trend analysis will no longer be available
</div>
</div>
<div class="impact-count"></div>
</li>
<li class="impact-item">
<div class="impact-icon">
<i class="fas fa-link"></i>
</div>
<div class="impact-content">
<div class="impact-title">Related Measurements</div>
<div class="impact-description">
Links to related measurements and benchmarks will be broken
</div>
</div>
<div class="impact-count">{{ measurement.related_measurements.count|default:0 }}</div>
</li>
<li class="impact-item">
<div class="impact-icon">
<i class="fas fa-history"></i>
</div>
<div class="impact-content">
<div class="impact-title">Audit Trail</div>
<div class="impact-description">
Complete activity history and audit trail will be permanently deleted
</div>
</div>
<div class="impact-count">{{ measurement.activities.count|default:0 }}</div>
</li>
</ul>
</div>
</div>
<!-- Confirmation Steps -->
<div class="confirmation-section">
<div class="confirmation-title">
<i class="fas fa-clipboard-check"></i>
Deletion Confirmation Process
</div>
<ol class="confirmation-steps">
<li class="confirmation-step">
<div class="step-number">1</div>
<div class="step-content">
<div class="step-title">Review Impact</div>
<div class="step-description">
Carefully review all data that will be permanently deleted above
</div>
</div>
</li>
<li class="confirmation-step">
<div class="step-number">2</div>
<div class="step-content">
<div class="step-title">Export Data</div>
<div class="step-description">
Export measurement data and reports for backup before deletion
</div>
</div>
</li>
<li class="confirmation-step">
<div class="step-number">3</div>
<div class="step-content">
<div class="step-title">Notify Team</div>
<div class="step-description">
Inform team members about the measurement deletion
</div>
</div>
</li>
<li class="confirmation-step">
<div class="step-number">4</div>
<div class="step-content">
<div class="step-title">Confirm Deletion</div>
<div class="step-description">
Check the confirmation box and proceed with deletion
</div>
</div>
</li>
</ol>
<form method="post" id="delete-form">
{% csrf_token %}
<div class="confirmation-checkbox">
<input type="checkbox" id="confirm-delete" name="confirm_delete" required>
<label for="confirm-delete" class="confirmation-text">
I understand that this action cannot be undone and will permanently delete
"{{ measurement.name }}" and all associated data.
</label>
</div>
<div class="form-group">
<label class="form-label">Type measurement name to confirm:</label>
<input type="text" class="form-control" id="measurement-name-confirm"
placeholder="Enter: {{ measurement.name }}" required>
<div class="form-text">Type the exact measurement name to enable deletion</div>
</div>
</form>
</div>
</div>
<!-- Sidebar -->
<div class="delete-sidebar">
<!-- Recent Data Points -->
{% if measurement.recent_data_points %}
<div class="section-card">
<div class="section-header">
<i class="fas fa-chart-bar"></i>
Recent Data Points
</div>
<div class="section-content">
{% for data_point in measurement.recent_data_points|slice:":5" %}
<div class="data-point-item">
<div class="data-point-icon">
<i class="fas fa-chart-line"></i>
</div>
<div class="data-point-info">
<div class="data-point-date">{{ data_point.date|date:"M d, Y" }}</div>
<div class="data-point-value">{{ data_point.value }} {{ measurement.unit_of_measure|default:"" }}</div>
</div>
<div class="data-point-meta">{{ data_point.achievement_percentage|default:0 }}%</div>
</div>
{% endfor %}
{% if measurement.data_points.count > 5 %}
<div class="text-center text-muted mt-2">
+{{ measurement.data_points.count|add:"-5" }} more data points
</div>
{% endif %}
</div>
</div>
{% endif %}
<!-- Performance Chart -->
<div class="section-card">
<div class="section-header">
<i class="fas fa-chart-area"></i>
Performance Overview
</div>
<div class="section-content">
<div class="chart-preview">
<div class="text-center">
<i class="fas fa-chart-line fa-2x mb-2"></i>
<p class="mb-0">Performance trend chart</p>
<small>{{ measurement.data_points.count }} data points</small>
</div>
</div>
<div class="measurement-info">
<div class="info-item">
<div class="info-label">Current Value</div>
<div class="info-value">{{ measurement.current_value|default:"No data" }}</div>
</div>
<div class="info-item">
<div class="info-label">Target</div>
<div class="info-value">{{ measurement.target_value|default:"Not set" }}</div>
</div>
<div class="info-item">
<div class="info-label">Achievement</div>
<div class="info-value">{{ measurement.achievement_percentage|default:0 }}%</div>
</div>
<div class="info-item">
<div class="info-label">Last Updated</div>
<div class="info-value">{{ measurement.last_data_date|date:"M d, Y"|default:"Never" }}</div>
</div>
</div>
</div>
</div>
<!-- Team Members -->
{% if measurement.team_members.exists %}
<div class="section-card">
<div class="section-header">
<i class="fas fa-users"></i>
Affected Team Members
</div>
<div class="section-content">
{% for member in measurement.team_members.all %}
<div class="team-member">
<div class="member-avatar">
{{ member.first_name.0|upper }}{{ member.last_name.0|upper }}
</div>
<div class="member-info">
<div class="member-name">{{ member.get_full_name }}</div>
<div class="member-role">{{ member.email }}</div>
</div>
</div>
{% endfor %}
</div>
</div>
{% endif %}
<!-- Alternative Actions -->
<div class="section-card">
<div class="section-header">
<i class="fas fa-lightbulb"></i>
Alternative Actions
</div>
<div class="section-content">
<div class="alternative-text">
Consider these alternatives to permanent deletion:
</div>
<div class="alternative-actions">
<button type="button" class="btn btn-outline-warning btn-sm" onclick="deactivateMeasurement()">
<i class="fas fa-pause me-1"></i>Deactivate Measurement
</button>
<button type="button" class="btn btn-outline-info btn-sm" onclick="archiveMeasurement()">
<i class="fas fa-archive me-1"></i>Archive Measurement
</button>
<button type="button" class="btn btn-outline-success btn-sm" onclick="exportMeasurement()">
<i class="fas fa-download me-1"></i>Export Data
</button>
<a href="{% url 'quality:measurement_edit' measurement.id %}" class="btn btn-outline-primary btn-sm">
<i class="fas fa-edit me-1"></i>Edit Instead
</a>
</div>
</div>
</div>
<!-- Deletion History -->
<div class="section-card">
<div class="section-header">
<i class="fas fa-history"></i>
Recent Deletions
</div>
<div class="section-content">
{% for deleted_measurement in recent_deletions %}
<div class="data-point-item">
<div class="data-point-icon" style="background: #6c757d;">
<i class="fas fa-trash"></i>
</div>
<div class="data-point-info">
<div class="data-point-date">{{ deleted_measurement.name|truncatechars:25 }}</div>
<div class="data-point-value">Deleted {{ deleted_measurement.deleted_at|timesince }} ago</div>
</div>
</div>
{% empty %}
<div class="text-center py-3 text-muted">
<i class="fas fa-history fa-2x mb-2"></i>
<p>No recent deletions</p>
</div>
{% endfor %}
</div>
</div>
</div>
</div>
<!-- Delete Actions -->
<div class="delete-actions">
<div class="alternative-text">
<strong>Need help?</strong> Contact your system administrator if you're unsure about deleting this measurement.
</div>
<div class="btn-group">
<a href="{% url 'quality:measurement_detail' measurement.id %}" class="btn btn-outline-secondary">
<i class="fas fa-arrow-left me-1"></i>Cancel
</a>
<button type="button" class="btn btn-outline-info" onclick="exportMeasurement()">
<i class="fas fa-download me-1"></i>Export First
</button>
<button type="submit" form="delete-form" class="btn btn-danger" id="delete-btn" disabled>
<i class="fas fa-trash me-1"></i>Delete Measurement
</button>
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
<script>
$(document).ready(function() {
// Enable delete button only when conditions are met
function checkDeleteConditions() {
const confirmChecked = $('#confirm-delete').is(':checked');
const nameMatch = $('#measurement-name-confirm').val() === '{{ measurement.name }}';
$('#delete-btn').prop('disabled', !(confirmChecked && nameMatch));
}
$('#confirm-delete, #measurement-name-confirm').on('change keyup', checkDeleteConditions);
// Form submission confirmation
$('#delete-form').on('submit', function(e) {
if (!confirm('Are you absolutely sure you want to delete this measurement? This action cannot be undone.')) {
e.preventDefault();
}
});
});
function deactivateMeasurement() {
if (confirm('Deactivate this measurement instead of deleting it?')) {
fetch(`/quality/measurements/{{ measurement.id }}/deactivate/`, {
method: 'POST',
headers: {
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
showAlert('Measurement deactivated successfully', 'success');
setTimeout(() => {
window.location.href = '{% url "quality:measurement_detail" measurement.id %}';
}, 1500);
} else {
showAlert('Error deactivating measurement', 'danger');
}
})
.catch(error => {
showAlert('Error deactivating measurement', 'danger');
});
}
}
function archiveMeasurement() {
if (confirm('Archive this measurement instead of deleting it?')) {
fetch(`/quality/measurements/{{ measurement.id }}/archive/`, {
method: 'POST',
headers: {
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
showAlert('Measurement archived successfully', 'success');
setTimeout(() => {
window.location.href = '{% url "quality:measurement_detail" measurement.id %}';
}, 1500);
} else {
showAlert('Error archiving measurement', 'danger');
}
})
.catch(error => {
showAlert('Error archiving measurement', 'danger');
});
}
}
function exportMeasurement() {
showAlert('Preparing measurement export...', 'info');
window.open(`/quality/measurements/{{ measurement.id }}/export/`, '_blank');
}
function showAlert(message, type) {
const alertDiv = document.createElement('div');
alertDiv.className = `alert alert-${type} alert-dismissible fade show position-fixed`;
alertDiv.style.cssText = 'top: 20px; right: 20px; z-index: 1060; min-width: 300px;';
alertDiv.innerHTML = `
${message}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
`;
document.body.appendChild(alertDiv);
setTimeout(() => {
if (alertDiv.parentNode) {
alertDiv.remove();
}
}, 5000);
}
</script>
{% endblock %}