This commit is contained in:
Marwan Alwali 2025-11-04 13:44:58 +03:00
parent 4e10003711
commit f6329ffa10
59 changed files with 2541 additions and 101 deletions

View File

@ -63,7 +63,6 @@ INSTALLED_APPS = [
'phonenumber_field',
'django_celery_beat',
'django_celery_results',
'debug_toolbar',
'django_extensions',
'webpack_loader',
@ -96,7 +95,6 @@ MIDDLEWARE = [
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django_htmx.middleware.HtmxMiddleware',
'simple_history.middleware.HistoryRequestMiddleware',
'debug_toolbar.middleware.DebugToolbarMiddleware',
]
ROOT_URLCONF = 'AgdarCentre.urls'
@ -412,14 +410,6 @@ LOGGING = {
}
# Debug Toolbar
# https://django-debug-toolbar.readthedocs.io/
INTERNAL_IPS = [
'127.0.0.1',
]
# Session Configuration
SESSION_COOKIE_AGE = 86400 # 24 hours
SESSION_SAVE_EVERY_REQUEST = True

View File

@ -49,8 +49,3 @@ urlpatterns += i18n_patterns(
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
import debug_toolbar
urlpatterns = [
path('__debug__/', include(debug_toolbar.urls)),
] + urlpatterns

View File

@ -455,7 +455,7 @@ $(document).ready(function() {
const formCount = parseInt($(totalFormsId).val()) || 0;
if (formCount >= maxForms) {
alert('{% trans "Maximum number of behaviors reached" %}');
showAlertModal('{% trans "Maximum number of behaviors reached" %}', 'warning');
return;
}
@ -539,7 +539,7 @@ $(document).ready(function() {
// Check if there's only one visible behavior left
const visibleBehaviors = $('.behavior-formset-item:visible').length;
if (visibleBehaviors <= 1) {
alert('{% trans "You must have at least one behavior" %}');
showAlertModal('{% trans "You must have at least one behavior" %}', 'warning');
return;
}
@ -573,7 +573,7 @@ $(document).ready(function() {
if (activeBehaviors === 0) {
e.preventDefault();
alert('{% trans "Please add at least one behavior" %}');
showAlertModal('{% trans "Please add at least one behavior" %}', 'warning');
hasError = true;
}

View File

@ -225,7 +225,7 @@
<i class="fas fa-exclamation-triangle me-2"></i>{% trans "This session has not been signed yet" %}
</div>
{% if user.role == 'ADMIN' or user == session.provider %}
<form method="post" action="{% url 'aba:session_sign' session.pk %}" onsubmit="return confirm('{% trans "Are you sure you want to sign this session? This action cannot be undone." %}');">
<form method="post" action="{% url 'aba:session_sign' session.pk %}" onsubmit="event.preventDefault(); showConfirmModal('{% trans "Are you sure you want to sign this session? This action cannot be undone." %}', '{% trans "Confirm Signature" %}').then((confirmed) => { if (confirmed) this.submit(); });">
{% csrf_token %}
<button type="submit" class="btn btn-success w-100">
<i class="fas fa-signature me-2"></i>{% trans "Sign Session" %}

View File

@ -290,7 +290,7 @@
const visibleSkills = $('#skill-target-formset .formset-row:visible').length;
if (visibleSkills === 0) {
e.preventDefault();
alert('{% trans "Please add at least one skill target" %}');
showAlertModal('{% trans "Please add at least one skill target" %}', 'warning');
return false;
}
});

View File

@ -450,7 +450,8 @@
// Date select (for quick booking)
select: function(info) {
{% if user.role in 'ADMIN,FRONT_DESK' %}
if (confirm('{% trans "Book an appointment for" %} ' + info.startStr + '?')) {
showConfirmModal('{% trans "Book an appointment for" %} ' + info.startStr + '?', '{% trans "Confirm Booking" %}').then((confirmed) => {
if (confirmed) {
var url = '{% url "appointments:appointment_create" %}' +
'?date=' + info.startStr.split('T')[0] +
'&time=' + info.startStr.split('T')[1].substring(0, 5);

View File

@ -466,7 +466,7 @@
{% if user.role in 'ADMIN,FRONT_DESK' %}
<form method="post" action="{% url 'appointments:appointment_no_show' appointment.pk %}">
{% csrf_token %}
<button type="submit" class="btn btn-dark w-100" onclick="return confirm('{% trans "Mark this appointment as no-show?" %}')">
<button type="submit" class="btn btn-dark w-100" onclick="event.preventDefault(); showConfirmModal('{% trans "Mark this appointment as no-show?" %}', '{% trans "Confirm Action" %}').then((confirmed) => { if (confirmed) this.form.submit(); });">
<i class="fas fa-user-slash me-1"></i>{% trans "Mark as No-Show" %}
</button>
</form>

View File

@ -454,7 +454,7 @@
if (!isValid) {
e.preventDefault();
alert(errorMessages.join('\n'));
showAlertModal(errorMessages.join('<br>'), 'warning');
}
});

View File

@ -248,33 +248,35 @@
if (!newDate || !newTime) {
e.preventDefault();
alert('{% trans "Please select both new date and time" %}');
showAlertModal('{% trans "Please select both new date and time" %}', 'warning');
return false;
}
if (!reason) {
e.preventDefault();
alert('{% trans "Please provide a reason for rescheduling" %}');
showAlertModal('{% trans "Please provide a reason for rescheduling" %}', 'warning');
return false;
}
// Check if date/time actually changed
if (newDate === currentDate && newTime === currentTime) {
e.preventDefault();
alert('{% trans "Please select a different date or time" %}');
showAlertModal('{% trans "Please select a different date or time" %}', 'warning');
return false;
}
// Confirm action
var confirmMsg = '{% trans "Are you sure you want to reschedule this appointment?" %}\n\n' +
'{% trans "Current" %}: ' + currentDate + ' ' + currentTime + '\n' +
'{% trans "New" %}: ' + newDate + ' ' + newTime;
var confirmMsg = '{% trans "Are you sure you want to reschedule this appointment?" %}<br><br>' +
'<strong>{% trans "Current" %}:</strong> ' + currentDate + ' ' + currentTime + '<br>' +
'<strong>{% trans "New" %}:</strong> ' + newDate + ' ' + newTime;
if (!confirm(confirmMsg)) {
e.preventDefault();
return false;
showConfirmModal(confirmMsg, '{% trans "Confirm Reschedule" %}').then((confirmed) => {
if (confirmed) {
this.submit();
}
});
});
});
</script>
{% endblock %}

View File

@ -463,12 +463,12 @@ document.addEventListener('DOMContentLoaded', function() {
e.preventDefault();
if (signaturePad.isEmpty()) {
alert('{% trans "Please provide your signature before submitting." %}');
showAlertModal('{% trans "Please provide your signature before submitting." %}', 'warning');
return;
}
if (!signedByNameInput.value.trim()) {
alert('{% trans "Please enter your full name." %}');
showAlertModal('{% trans "Please enter your full name." %}', 'warning');
signedByNameInput.focus();
return;
}

View File

@ -189,7 +189,7 @@ document.addEventListener('DOMContentLoaded', function() {
if (!consentType.value || !titleEn.value || !contentEn.value) {
e.preventDefault();
alert('{% trans "Please fill in all required fields" %}');
showAlertModal('{% trans "Please fill in all required fields" %}', 'warning');
return false;
}
});

View File

@ -283,7 +283,7 @@
const signatureData = document.getElementById('signatureData').value;
if (!signatureData) {
e.preventDefault();
alert('{% trans "Please draw your signature before submitting." %}');
showAlertModal('{% trans "Please draw your signature before submitting." %}', 'warning');
return false;
}
}

View File

@ -1,5 +1,5 @@
{% extends "base.html" %}
{% load i18n static %}
{% load i18n static tenant_tags %}
{% block title %}{% trans "Home" %} - {{ block.super }}{% endblock %}
@ -8,8 +8,14 @@
<!-- Welcome Section -->
<div class="text-center mb-2">
<h1 class="display-4 mb-2">
<img src="{% static 'img/logo/Agdar-Logo.png' %}" alt="Hospital Logo" style="height: 150px;"><br>
{% get_tenant_logo request.user.tenant as tenant_logo %}
{% if tenant_logo %}
<img src="{{ tenant_logo.url }}" alt="{{ request.user.tenant.name }}" style="height: 150px;" /><br>
{% trans "Welcome to" %} {{ request.user.tenant.name }}
{% else %}
<img src="{% static 'img/logo/tenhal_logo.png' %}" alt="Tenhal|تنحل" style="height: 150px;" /><br>
{% trans "Welcome to Agdar" %}
{% endif %}
</h1>
<p class="lead text-muted">{% trans "Behavioral Clinic Management System" %}</p>
</div>

View File

@ -21,11 +21,11 @@
<form method="post" action="{% url 'core:user_deactivate' staff_member.pk %}" class="d-inline">
{% csrf_token %}
{% if staff_member.is_active %}
<button type="submit" class="btn btn-danger" onclick="return confirm('{% trans "Are you sure you want to deactivate this staff member?" %}')">
<button type="submit" class="btn btn-danger" onclick="event.preventDefault(); showConfirmModal('{% trans "Are you sure you want to deactivate this staff member?" %}', '{% trans "Confirm Deactivation" %}').then((confirmed) => { if (confirmed) this.form.submit(); });">
<i class="fas fa-user-times"></i> {% trans "Deactivate" %}
</button>
{% else %}
<button type="submit" class="btn btn-success" onclick="return confirm('{% trans "Are you sure you want to activate this staff member?" %}')">
<button type="submit" class="btn btn-success" onclick="event.preventDefault(); showConfirmModal('{% trans "Are you sure you want to activate this staff member?" %}', '{% trans "Confirm Activation" %}').then((confirmed) => { if (confirmed) this.form.submit(); });">
<i class="fas fa-user-check"></i> {% trans "Activate" %}
</button>
{% endif %}

View File

@ -328,8 +328,11 @@
const action = this.querySelector('button').textContent.includes('Deactivate') ?
'deactivate' : 'activate';
if (!confirm(`Are you sure you want to ${action} ${staffName}?`)) {
e.preventDefault();
showConfirmModal(`Are you sure you want to ${action} ${staffName}?`, 'Confirm Action').then((confirmed) => {
if (confirmed) {
form.submit();
}
}
});
});

View File

@ -345,13 +345,17 @@
const clockInOutForm = document.querySelector('form[action*="clock-in-out"]');
if (clockInOutForm) {
clockInOutForm.addEventListener('submit', function(e) {
e.preventDefault();
const button = this.querySelector('button[type="submit"]');
const action = button.textContent.includes('Clock Out') ? 'clock out' : 'clock in';
const form = this;
if (!confirm(`Are you sure you want to ${action}?`)) {
e.preventDefault();
showConfirmModal(`Are you sure you want to ${action}?`, 'Confirm Action').then((confirmed) => {
if (confirmed) {
form.submit();
}
});
});
}
// Animate profile completion progress bar

View File

@ -264,7 +264,7 @@
if (file) {
// Validate file size (max 5MB)
if (file.size > 5 * 1024 * 1024) {
alert('{% trans "File size must be less than 5MB" %}');
showAlertModal('{% trans "File size must be less than 5MB" %}', 'warning');
this.value = '';
return;
}
@ -272,7 +272,7 @@
// Validate file type
const validTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/gif'];
if (!validTypes.includes(file.type)) {
alert('{% trans "Please select a valid image file (JPEG, PNG, or GIF)" %}');
showAlertModal('{% trans "Please select a valid image file (JPEG, PNG, or GIF)" %}', 'warning');
this.value = '';
return;
}
@ -319,7 +319,7 @@
if (!isValid) {
e.preventDefault();
alert('{% trans "Please fill in all required fields" %}');
showAlertModal('{% trans "Please fill in all required fields" %}', 'warning');
}
});
}

View File

@ -0,0 +1,93 @@
"""
Template tags for tenant-related functionality.
"""
from django import template
from django.core.cache import cache
from core.settings_service import get_tenant_settings_service
register = template.Library()
@register.simple_tag
def get_tenant_setting(tenant, key, default=None):
"""
Get a tenant setting value.
Usage:
{% get_tenant_setting request.user.tenant 'basic_logo' as logo %}
{% if logo %}
<img src="{{ logo.url }}" alt="Logo" />
{% endif %}
Args:
tenant: Tenant instance
key: Setting key
default: Default value if setting doesn't exist
Returns:
Setting value
"""
if not tenant:
return default
try:
settings_service = get_tenant_settings_service(tenant)
return settings_service.get_setting(key, default)
except Exception:
return default
@register.simple_tag
def get_tenant_logo(tenant):
"""
Get tenant logo file.
Usage:
{% get_tenant_logo request.user.tenant as logo %}
{% if logo %}
<img src="{{ logo.url }}" alt="{{ tenant.name }}" />
{% endif %}
Args:
tenant: Tenant instance
Returns:
Logo file or None
"""
if not tenant:
return None
try:
settings_service = get_tenant_settings_service(tenant)
logo = settings_service.get_setting('basic_logo')
return logo if logo else None
except Exception:
return None
@register.filter
def has_tenant_logo(tenant):
"""
Check if tenant has a logo.
Usage:
{% if request.user.tenant|has_tenant_logo %}
...
{% endif %}
Args:
tenant: Tenant instance
Returns:
Boolean
"""
if not tenant:
return False
try:
settings_service = get_tenant_settings_service(tenant)
logo = settings_service.get_setting('basic_logo')
return bool(logo)
except Exception:
return False

Binary file not shown.

View File

@ -167,7 +167,7 @@
<div class="col-md-2 text-end">
<label class="form-label small d-block">&nbsp;</label>
{% if not service_form.instance.pk or service_formset.can_delete %}
<button type="button" class="btn btn-danger btn-sm btn-remove-service">
<button type="button" class="btn btn-outline-danger btn-sm btn-remove-service">
<i class="fas fa-trash"></i>
</button>
{% endif %}
@ -231,7 +231,7 @@
</div>
<div class="col-md-2 text-end">
<label class="form-label small d-block">&nbsp;</label>
<button type="button" class="btn btn-danger btn-sm btn-remove-service">
<button type="button" class="btn btn-outline-danger btn-sm btn-remove-service">
<i class="fas fa-trash"></i>
</button>
{{ service_formset.empty_form.id }}

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -428,7 +428,7 @@
<i class="fas fa-exclamation-triangle me-2"></i>{% trans "This consultation has not been signed yet" %}
</div>
{% if user.role == 'ADMIN' or user == consultation.provider %}
<form method="post" action="{% url 'medical:consultation_sign' consultation.pk %}" onsubmit="return confirm('{% trans "Are you sure you want to sign this consultation? This action cannot be undone." %}');">
<form method="post" action="{% url 'medical:consultation_sign' consultation.pk %}" onsubmit="event.preventDefault(); showConfirmModal('{% trans "Are you sure you want to sign this consultation? This action cannot be undone." %}', '{% trans "Confirm Signature" %}').then((confirmed) => { if (confirmed) this.submit(); });">
{% csrf_token %}
<button type="submit" class="btn btn-success w-100">
<i class="fas fa-signature me-2"></i>{% trans "Sign Consultation" %}

View File

@ -453,7 +453,7 @@
$(this).find('h6').html(`<i class="fas fa-capsules me-2"></i>{% trans "Medication" %} #${index + 1}`);
});
} else {
alert('{% trans "At least one medication is required" %}');
showAlertModal('{% trans "At least one medication is required" %}', 'warning');
}
});
@ -507,7 +507,7 @@
const patientId = $('input[name="patient"]').val();
if (!patientId) {
e.preventDefault();
alert('{% trans "Patient is required. Please access this form from a patient record." %}');
showAlertModal('{% trans "Patient is required. Please access this form from a patient record." %}', 'error');
return false;
}
@ -516,7 +516,7 @@
const medicationRows = $('.medication-row').length;
if (medicationRows > 0 && medications.length === 0) {
e.preventDefault();
alert('{% trans "Please complete all medication fields (drug name, dose, frequency) or remove empty medications" %}');
showAlertModal('{% trans "Please complete all medication fields (drug name, dose, frequency) or remove empty medications" %}', 'warning');
return false;
}
});

View File

@ -233,7 +233,7 @@
<i class="fas fa-exclamation-triangle me-2"></i>{% trans "This follow-up has not been signed yet" %}
</div>
{% if user.role == 'ADMIN' or user == followup.provider %}
<form method="post" action="{% url 'medical:followup_sign' followup.pk %}" onsubmit="return confirm('{% trans "Are you sure you want to sign this follow-up? This action cannot be undone." %}');">
<form method="post" action="{% url 'medical:followup_sign' followup.pk %}" onsubmit="event.preventDefault(); showConfirmModal('{% trans "Are you sure you want to sign this follow-up? This action cannot be undone." %}', '{% trans "Confirm Signature" %}').then((confirmed) => { if (confirmed) this.submit(); });">
{% csrf_token %}
<button type="submit" class="btn btn-success w-100">
<i class="fas fa-signature me-2"></i>{% trans "Sign Follow-up" %}

View File

@ -384,7 +384,7 @@
$(this).find('h6').html(`<i class="fas fa-clipboard-list me-2"></i>{% trans "Complaint" %} #${index + 1}`);
});
} else {
alert('{% trans "At least one complaint entry is required" %}');
showAlertModal('{% trans "At least one complaint entry is required" %}', 'warning');
}
});
@ -429,7 +429,7 @@
if (!hasValidComplaint) {
e.preventDefault();
alert('{% trans "Please add at least one complaint with description" %}');
showAlertModal('{% trans "Please add at least one complaint with description" %}', 'warning');
return false;
}
});

View File

@ -70,7 +70,7 @@
<div>
{% if can_retry %}
<form method="post" action="{% url 'notifications:message_retry' message.pk %}"
class="d-inline" onsubmit="return confirm('{% trans "Retry sending this message?" %}');">
class="d-inline" onsubmit="event.preventDefault(); showConfirmModal('{% trans "Retry sending this message?" %}', '{% trans "Confirm Retry" %}').then((confirmed) => { if (confirmed) this.submit(); });">
{% csrf_token %}
<button type="submit" class="btn btn-warning">
<i class="fas fa-redo me-1"></i>{% trans "Retry" %}

View File

@ -78,7 +78,7 @@
</a>
{% if message.can_retry %}
<form method="post" action="{% url 'notifications:message_retry' message.pk %}"
class="d-inline" onsubmit="return confirm('{% trans "Retry sending this message?" %}');">
class="d-inline" onsubmit="event.preventDefault(); showConfirmModal('{% trans "Retry sending this message?" %}', '{% trans "Confirm Retry" %}').then((confirmed) => { if (confirmed) this.submit(); });">
{% csrf_token %}
<button type="submit" class="btn btn-outline-warning"
data-bs-toggle="tooltip" title="{% trans 'Retry' %}">

View File

@ -337,7 +337,7 @@
<i class="fas fa-exclamation-triangle me-2"></i>{% trans "This encounter has not been signed yet" %}
</div>
{% if user.role == 'ADMIN' or user == encounter.filled_by %}
<form method="post" action="{% url 'nursing:encounter_sign' encounter.pk %}" onsubmit="return confirm('{% trans "Are you sure you want to sign this encounter? This action cannot be undone." %}');">
<form method="post" action="{% url 'nursing:encounter_sign' encounter.pk %}" onsubmit="event.preventDefault(); showConfirmModal('{% trans "Are you sure you want to sign this encounter? This action cannot be undone." %}', '{% trans "Confirm Signature" %}').then((confirmed) => { if (confirmed) this.submit(); });">
{% csrf_token %}
<button type="submit" class="btn btn-success w-100">
<i class="fas fa-signature me-2"></i>{% trans "Sign Encounter" %}

View File

@ -5,7 +5,6 @@
{% block css %}
<link href="{% static 'plugins/select2/css/select2.min.css' %}" rel="stylesheet" />
<link href="{% static 'plugins/select2-bootstrap-5-theme/select2-bootstrap-5-theme.min.css' %}" rel="stylesheet" />
{% endblock %}
{% block content %}
@ -435,7 +434,7 @@
if (!hasVitals && !hasAnthro) {
e.preventDefault();
alert('{% trans "Please enter at least one vital sign or anthropometric measurement" %}');
showAlertModal('{% trans "Please enter at least one vital sign or anthropometric measurement" %}', 'warning');
return false;
}
});

View File

@ -169,7 +169,7 @@
<i class="fas fa-exclamation-triangle me-2"></i>{% trans "This consultation has not been signed yet" %}
</div>
{% if user.role == 'ADMIN' or user == consult.provider %}
<form method="post" action="{% url 'ot:consult_sign' consult.pk %}" onsubmit="return confirm('{% trans "Are you sure you want to sign this consultation? This action cannot be undone." %}');">
<form method="post" action="{% url 'ot:consult_sign' consult.pk %}" onsubmit="event.preventDefault(); showConfirmModal('{% trans "Are you sure you want to sign this consultation? This action cannot be undone." %}', '{% trans "Confirm Signature" %}').then((confirmed) => { if (confirmed) this.submit(); });">
{% csrf_token %}
<button type="submit" class="btn btn-success w-100">
<i class="fas fa-signature me-2"></i>{% trans "Sign Consultation" %}

View File

@ -167,7 +167,7 @@
<i class="fas fa-exclamation-triangle me-2"></i>{% trans "This session has not been signed yet" %}
</div>
{% if user.role == 'ADMIN' or user == session.provider %}
<form method="post" action="{% url 'ot:session_sign' session.pk %}" onsubmit="return confirm('{% trans "Are you sure you want to sign this session? This action cannot be undone." %}');">
<form method="post" action="{% url 'ot:session_sign' session.pk %}" onsubmit="event.preventDefault(); showConfirmModal('{% trans "Are you sure you want to sign this session? This action cannot be undone." %}', '{% trans "Confirm Signature" %}').then((confirmed) => { if (confirmed) this.submit(); });">
{% csrf_token %}
<button type="submit" class="btn btn-success w-100">
<i class="fas fa-signature me-2"></i>{% trans "Sign Session" %}

View File

@ -301,7 +301,7 @@
$(this).closest('.activity-row').remove();
updateActivitiesJson();
} else {
alert('{% trans "At least one activity is required" %}');
showAlertModal('{% trans "At least one activity is required" %}', 'warning');
}
});
@ -410,7 +410,7 @@
$(this).find('h6').html(`<i class="fas fa-target me-2"></i>{% trans "Skill" %} #${index + 1}`);
});
} else {
alert('{% trans "At least one skill is required" %}');
showAlertModal('{% trans "At least one skill is required" %}', 'warning');
}
});
@ -451,7 +451,7 @@
const patientId = $('input[name="patient"]').val();
if (!patientId) {
e.preventDefault();
alert('{% trans "Patient is required. Please access this form from a patient record." %}');
showAlertModal('{% trans "Patient is required. Please access this form from a patient record." %}', 'error');
return false;
}
@ -459,7 +459,7 @@
const activities = JSON.parse(activitiesJsonField.val() || '[]');
if (activities.length === 0) {
e.preventDefault();
alert('{% trans "Please add at least one activity" %}');
showAlertModal('{% trans "Please add at least one activity" %}', 'warning');
return false;
}
@ -467,7 +467,7 @@
const skills = JSON.parse(skillsJsonField.val() || '[]');
if (skills.length === 0) {
e.preventDefault();
alert('{% trans "Please add at least one target skill" %}');
showAlertModal('{% trans "Please add at least one target skill" %}', 'warning');
return false;
}
});

View File

@ -48,7 +48,6 @@ dependencies = [
# FHIR (for NPHIES)
"fhir.resources>=7.1.0",
# Development Tools
"django-debug-toolbar>=4.2.0",
"django-extensions>=3.2.3",
"ipython>=8.20.0",
# Testing

View File

@ -237,7 +237,7 @@
error: function(xhr, status, error) {
console.error('Error fetching providers:', error);
$providerSelect.prop('disabled', false);
alert('{% trans "Error loading providers. Please try again." %}');
showAlertModal('{% trans "Error loading providers. Please try again." %}', 'error');
}
});
});

View File

@ -180,7 +180,7 @@
<i class="fas fa-exclamation-triangle me-2"></i>{% trans "This assessment has not been signed yet" %}
</div>
{% if user.role == 'ADMIN' or user == assessment.provider %}
<form method="post" action="{% url 'slp:assessment_sign' assessment.pk %}" onsubmit="return confirm('{% trans "Are you sure you want to sign this assessment? This action cannot be undone." %}');">
<form method="post" action="{% url 'slp:assessment_sign' assessment.pk %}" onsubmit="event.preventDefault(); showConfirmModal('{% trans "Are you sure you want to sign this assessment? This action cannot be undone." %}', '{% trans "Confirm Signature" %}').then((confirmed) => { if (confirmed) this.submit(); });">
{% csrf_token %}
<button type="submit" class="btn btn-success w-100">
<i class="fas fa-signature me-2"></i>{% trans "Sign Assessment" %}

View File

@ -178,7 +178,7 @@
<i class="fas fa-exclamation-triangle me-2"></i>{% trans "This consultation has not been signed yet" %}
</div>
{% if user.role == 'ADMIN' or user == consultation.provider %}
<form method="post" action="{% url 'slp:consult_sign' consultation.pk %}" onsubmit="return confirm('{% trans "Are you sure you want to sign this consultation? This action cannot be undone." %}');">
<form method="post" action="{% url 'slp:consult_sign' consultation.pk %}" onsubmit="event.preventDefault(); showConfirmModal('{% trans "Are you sure you want to sign this consultation? This action cannot be undone." %}', '{% trans "Confirm Signature" %}').then((confirmed) => { if (confirmed) this.submit(); });">
{% csrf_token %}
<button type="submit" class="btn btn-success w-100">
<i class="fas fa-signature me-2"></i>{% trans "Sign Consultation" %}

View File

@ -109,7 +109,7 @@
<i class="fas fa-exclamation-triangle me-2"></i>{% trans "This intervention has not been signed yet" %}
</div>
{% if user.role == 'ADMIN' or user == intervention.provider %}
<form method="post" action="{% url 'slp:intervention_sign' intervention.pk %}" onsubmit="return confirm('{% trans "Are you sure you want to sign this intervention? This action cannot be undone." %}');">
<form method="post" action="{% url 'slp:intervention_sign' intervention.pk %}" onsubmit="event.preventDefault(); showConfirmModal('{% trans "Are you sure you want to sign this intervention? This action cannot be undone." %}', '{% trans "Confirm Signature" %}').then((confirmed) => { if (confirmed) this.submit(); });">
{% csrf_token %}
<button type="submit" class="btn btn-success w-100">
<i class="fas fa-signature me-2"></i>{% trans "Sign Intervention" %}

View File

@ -255,7 +255,7 @@
$(this).find('h6').html(`<i class="fas fa-target me-2"></i>{% trans "Target" %} #${index + 1}`);
});
} else {
alert('{% trans "At least one target is required" %}');
showAlertModal('{% trans "At least one target is required" %}', 'warning');
}
});
@ -301,7 +301,7 @@
const targets = JSON.parse(targetsJsonField.val() || '[]');
if (targets.length === 0) {
e.preventDefault();
alert('{% trans "Please add at least one intervention target" %}');
showAlertModal('{% trans "Please add at least one intervention target" %}', 'warning');
return false;
}
@ -315,7 +315,7 @@
if (hasInvalidTarget) {
e.preventDefault();
alert('{% trans "Each target must have Subjective, Objective, Assessment, and Plan filled in" %}');
showAlertModal('{% trans "Each target must have Subjective, Objective, Assessment, and Plan filled in" %}', 'warning');
return false;
}
});

View File

@ -162,7 +162,7 @@
<i class="fas fa-exclamation-triangle me-2"></i>{% trans "This progress report has not been signed yet" %}
</div>
{% if user.role == 'ADMIN' or user == report.provider %}
<form method="post" action="{% url 'slp:progress_report_sign' report.pk %}" onsubmit="return confirm('{% trans "Are you sure you want to sign this progress report? This action cannot be undone." %}');">
<form method="post" action="{% url 'slp:progress_report_sign' report.pk %}" onsubmit="event.preventDefault(); showConfirmModal('{% trans "Are you sure you want to sign this progress report? This action cannot be undone." %}', '{% trans "Confirm Signature" %}').then((confirmed) => { if (confirmed) this.submit(); });">
{% csrf_token %}
<button type="submit" class="btn btn-success w-100">
<i class="fas fa-signature me-2"></i>{% trans "Sign Progress Report" %}

View File

@ -268,7 +268,7 @@
$(this).closest('.objective-row').remove();
updateObjectivesJson();
} else {
alert('{% trans "At least one objective is required" %}');
showAlertModal('{% trans "At least one objective is required" %}', 'warning');
}
});
@ -303,7 +303,7 @@
const objectives = JSON.parse(objectivesJsonField.val() || '{}');
if (Object.keys(objectives).length === 0) {
e.preventDefault();
alert('{% trans "Please add at least one objective with progress" %}');
showAlertModal('{% trans "Please add at least one objective with progress" %}', 'warning');
return false;
}
@ -311,7 +311,7 @@
const patientId = $('input[name="patient"]').val();
if (!patientId) {
e.preventDefault();
alert('{% trans "Patient is required. Please access this form from a patient record." %}');
showAlertModal('{% trans "Patient is required. Please access this form from a patient record." %}', 'error');
return false;
}
});

BIN
static/.DS_Store vendored

Binary file not shown.

BIN
static/img/.DS_Store vendored

Binary file not shown.

BIN
static/img/login-bg/.DS_Store vendored Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

View File

@ -93,6 +93,7 @@
{% include 'partial/theme-panel.html' %}
</div>
{% include 'partial/scroll-top-btn.html' %}
{% include 'partial/alert_modal.html' %}
{% block outter_content %}
{% endblock %}

View File

@ -185,7 +185,7 @@
</div>
<div class="card-body">
{% if note.is_locked %}
<form method="post" action="{% url 'documents:note-unlock' note.pk %}" onsubmit="return confirm('{% trans "Are you sure you want to unlock this note?" %}');">
<form method="post" action="{% url 'documents:note-unlock' note.pk %}" onsubmit="event.preventDefault(); showConfirmModal('{% trans "Are you sure you want to unlock this note?" %}', '{% trans "Confirm Unlock" %}').then((confirmed) => { if (confirmed) this.submit(); });">
{% csrf_token %}
<button type="submit" class="btn btn-warning w-100">
<i class="fa fa-unlock me-1"></i>{% trans "Unlock Note" %}
@ -195,7 +195,7 @@
</small>
</form>
{% else %}
<form method="post" action="{% url 'documents:note-lock' note.pk %}" onsubmit="return confirm('{% trans "Are you sure you want to lock this note? This action cannot be undone." %}');">
<form method="post" action="{% url 'documents:note-lock' note.pk %}" onsubmit="event.preventDefault(); showConfirmModal('{% trans "Are you sure you want to lock this note? This action cannot be undone." %}', '{% trans "Confirm Lock" %}').then((confirmed) => { if (confirmed) this.submit(); });">
{% csrf_token %}
<button type="submit" class="btn btn-danger w-100">
<i class="fa fa-lock me-1"></i>{% trans "Lock Note" %}

View File

@ -0,0 +1,194 @@
{% load i18n %}
<!-- Global Alert Modal -->
<style>
/* Ensure modal header has rounded corners */
#alertModal .modal-header {
border-top-left-radius: calc(0.5rem - 1px);
border-top-right-radius: calc(0.5rem - 1px);
}
#alertModal .modal-content {
border-radius: 0.5rem;
}
</style>
<div class="modal fade" id="alertModal" tabindex="-1" aria-labelledby="alertModalLabel" aria-hidden="true" data-bs-backdrop="static" data-bs-keyboard="true">
<div class="modal-dialog modal-sm modal-dialog-centered">
<div class="modal-content">
<div class="modal-header " id="alertModalHeader">
<h5 class="modal-title" id="alertModalLabel">
<i class="fas fa-exclamation-circle me-2" id="alertModalIcon"></i>
<span id="alertModalTitle">{% trans "Alert" %}</span>
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="{% trans 'Close' %}"></button>
</div>
<div class="modal-body" id="alertModalBody">
<!-- Message will be inserted here -->
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" data-bs-dismiss="modal" id="alertModalOkBtn">
{% trans "OK" %}
</button>
</div>
</div>
</div>
</div>
<script>
/**
* Show a Bootstrap modal alert instead of browser alert()
* @param {string} message - The message to display
* @param {string} type - The alert type: 'error', 'warning', 'info', 'success'
* @param {string} title - Optional custom title
* @returns {Promise} - Resolves when modal is closed
*/
function showAlertModal(message, type = 'warning', title = null) {
return new Promise((resolve) => {
const modal = document.getElementById('alertModal');
const modalHeader = document.getElementById('alertModalHeader');
const modalTitle = document.getElementById('alertModalTitle');
const modalIcon = document.getElementById('alertModalIcon');
const modalBody = document.getElementById('alertModalBody');
const modalOkBtn = document.getElementById('alertModalOkBtn');
// Set message
modalBody.innerHTML = message;
// Configure based on type
let iconClass, headerClass, titleText, btnClass;
switch(type) {
case 'error':
case 'danger':
iconClass = 'fas fa-times-circle';
headerClass = 'bg-danger text-white';
titleText = title || '{% trans "Error" %}';
btnClass = 'btn-danger';
break;
case 'warning':
iconClass = 'fas fa-exclamation-triangle';
headerClass = 'bg-warning text-dark';
titleText = title || '{% trans "Warning" %}';
btnClass = 'btn-warning';
break;
case 'info':
iconClass = 'fas fa-info-circle';
headerClass = 'bg-info text-white';
titleText = title || '{% trans "Information" %}';
btnClass = 'btn-info';
break;
case 'success':
iconClass = 'fas fa-check-circle';
headerClass = 'bg-success text-white';
titleText = title || '{% trans "Success" %}';
btnClass = 'btn-success';
break;
default:
iconClass = 'fas fa-exclamation-circle';
headerClass = 'bg-primary text-white';
titleText = title || '{% trans "Alert" %}';
btnClass = 'btn-primary';
}
// Apply styling
modalIcon.className = iconClass + ' me-2';
modalTitle.textContent = titleText;
modalHeader.className = 'modal-header ' + headerClass;
modalOkBtn.className = 'btn ' + btnClass;
// Show modal
const bsModal = new bootstrap.Modal(modal);
bsModal.show();
// Focus OK button when modal is shown
modal.addEventListener('shown.bs.modal', function() {
modalOkBtn.focus();
}, { once: true });
// Resolve promise when modal is hidden
modal.addEventListener('hidden.bs.modal', function() {
resolve();
}, { once: true });
});
}
/**
* Show a confirmation modal with Yes/No buttons
* @param {string} message - The message to display
* @param {string} title - Optional custom title
* @returns {Promise<boolean>} - Resolves to true if confirmed, false if cancelled
*/
function showConfirmModal(message, title = null) {
return new Promise((resolve) => {
const modal = document.getElementById('alertModal');
const modalHeader = document.getElementById('alertModalHeader');
const modalTitle = document.getElementById('alertModalTitle');
const modalIcon = document.getElementById('alertModalIcon');
const modalBody = document.getElementById('alertModalBody');
const modalFooter = modal.querySelector('.modal-footer');
// Set message
modalBody.innerHTML = message;
// Configure for confirmation
const iconClass = 'fas fa-question-circle';
const headerClass = 'bg-warning text-dark';
const titleText = title || '{% trans "Confirm Action" %}';
// Apply styling
modalIcon.className = iconClass + ' me-2';
modalTitle.textContent = titleText;
modalHeader.className = 'modal-header ' + headerClass;
// Replace footer with Yes/No buttons
modalFooter.innerHTML = `
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" id="confirmNoBtn">
{% trans "No" %}
</button>
<button type="button" class="btn btn-warning" id="confirmYesBtn">
{% trans "Yes" %}
</button>
`;
// Show modal
const bsModal = new bootstrap.Modal(modal);
bsModal.show();
// Track if user clicked Yes
let userConfirmed = false;
// Handle Yes button
document.getElementById('confirmYesBtn').addEventListener('click', function() {
userConfirmed = true;
bsModal.hide();
}, { once: true });
// Handle No button - explicitly set to false
document.getElementById('confirmNoBtn').addEventListener('click', function() {
userConfirmed = false;
}, { once: true });
// Handle modal close - resolve with the confirmation status
modal.addEventListener('hidden.bs.modal', function() {
// Restore original footer
modalFooter.innerHTML = `
<button type="button" class="btn btn-primary" data-bs-dismiss="modal" id="alertModalOkBtn">
{% trans "OK" %}
</button>
`;
resolve(userConfirmed);
}, { once: true });
// Focus Yes button when modal is shown
modal.addEventListener('shown.bs.modal', function() {
document.getElementById('confirmYesBtn').focus();
}, { once: true });
});
}
/**
* Backward compatibility - replace window.alert with modal
* Uncomment if you want to override all alert() calls globally
*/
// window.alert = function(message) {
// showAlertModal(message, 'info');
// };
</script>

View File

@ -1,4 +1,4 @@
{% load static i18n %}
{% load static i18n tenant_tags %}
<div id="header" class="app-header" data-bs-theme="{% if appHeaderInverse %}dark{% endif %}">
<!-- BEGIN navbar-header -->
<div class="navbar-header">
@ -9,7 +9,14 @@
<span class="icon-bar"></span>
</button>
{% endif %}
<a href="{% url 'core:dashboard' %}" class="navbar-brand"><img src="{% static 'img/logo/Agdar-Logo.png' %}" alt="" class="me-2" style="height: 64px; "></a>
<a href="{% url 'core:dashboard' %}" class="navbar-brand">
{% get_tenant_logo request.user.tenant as tenant_logo %}
{% if tenant_logo %}
<img src="{{ tenant_logo.url }}" alt="{{ request.user.tenant.name }}" style="height: 64px;" class="me-2" />
{% else %}
<img src="{% static 'img/logo/tenhal_logo.png' %}" alt="Tenhal|تنحل" class="me-2" style="height: 64px;" />
{% endif %}
</a>
{% if appHeaderMegaMenu %}
<button type="button" class="navbar-mobile-toggler collapsed" data-bs-toggle="collapse" data-bs-target="#top-navbar" aria-expanded="false">
<span class="fa-stack fa-lg">

View File

@ -1,6 +1,6 @@
{% extends 'base.html' %}
{% load static i18n %}
{% load static i18n tenant_tags %}
{% block title %}{% trans "Login" %}{% endblock %}
@ -13,7 +13,12 @@
<div class="login-header">
<div class="brand">
<div class="d-flex align-items-center">
<img src="{% static 'img/logo/Agdar-Logo.png' %}" alt="" class="me-2" style="height: 128px; ">
{% get_tenant_logo request.user.tenant as tenant_logo %}
{% if tenant_logo %}
<img src="{{ tenant_logo.url }}" alt="{{ request.user.tenant.name }}" style="height: 128px;" class="me-2" />
{% else %}
<img src="{% static 'img/logo/tenhal_logo.png' %}" alt="Tenhal|تنحل" class="me-2" style="height: 128px;" />
{% endif %}
</div>
<small>{% trans "Behavioral Clinic Management System" %}</small>
</div>

View File

@ -1,6 +1,6 @@
{% extends 'base.html' %}
{% load static i18n %}
{% load static i18n tenant_tags %}
{% block title %}{% trans "Password Reset Complete" %} - Agdar{% endblock %}
@ -13,7 +13,12 @@
<div class="login-header">
<div class="brand">
<div class="d-flex align-items-center">
<img src="{% static 'img/logo/Agdar-Logo.png' %}" alt="" class="me-2" style="height: 128px; ">
{% get_tenant_logo request.user.tenant as tenant_logo %}
{% if tenant_logo %}
<img src="{{ tenant_logo.url }}" alt="{{ request.user.tenant.name }}" style="height: 128px;" class="me-2" />
{% else %}
<img src="{% static 'img/logo/tenhal_logo.png' %}" alt="Tenhal|تنحل" class="me-2" style="height: 128px;" />
{% endif %}
</div>
<small>{% trans "Behavioral Clinic Management System" %}</small>
</div>

View File

@ -1,6 +1,6 @@
{% extends 'base.html' %}
{% load static i18n %}
{% load static i18n tenant_tags %}
{% block title %}{% trans "Set New Password" %} - Agdar{% endblock %}
@ -13,7 +13,12 @@
<div class="login-header">
<div class="brand">
<div class="d-flex align-items-center">
<img src="{% static 'img/logo/Agdar-Logo.png' %}" alt="" class="me-2" style="height: 128px; ">
{% get_tenant_logo request.user.tenant as tenant_logo %}
{% if tenant_logo %}
<img src="{{ tenant_logo.url }}" alt="{{ request.user.tenant.name }}" style="height: 128px;" class="me-2" />
{% else %}
<img src="{% static 'img/logo/tenhal_logo.png' %}" alt="Tenhal|تنحل" class="me-2" style="height: 128px;" />
{% endif %}
</div>
<small>{% trans "Behavioral Clinic Management System" %}</small>
</div>

View File

@ -1,6 +1,6 @@
{% extends 'base.html' %}
{% load static i18n %}
{% load static i18n tenant_tags %}
{% block title %}{% trans "Password Reset Sent" %} - Agdar{% endblock %}
@ -13,7 +13,12 @@
<div class="login-header">
<div class="brand">
<div class="d-flex align-items-center">
<img src="{% static 'img/logo/Agdar-Logo.png' %}" alt="" class="me-2" style="height: 128px; ">
{% get_tenant_logo request.user.tenant as tenant_logo %}
{% if tenant_logo %}
<img src="{{ tenant_logo.url }}" alt="{{ request.user.tenant.name }}" style="height: 128px;" class="me-2" />
{% else %}
<img src="{% static 'img/logo/tenhal_logo.png' %}" alt="Tenhal|تنحل" class="me-2" style="height: 128px;" />
{% endif %}
</div>
<small>{% trans "Behavioral Clinic Management System" %}</small>
</div>

View File

@ -1,6 +1,6 @@
{% extends 'base.html' %}
{% load static i18n %}
{% load static i18n tenant_tags %}
{% block title %}{% trans "Reset Password" %} - Agdar{% endblock %}
@ -13,7 +13,12 @@
<div class="login-header">
<div class="brand">
<div class="d-flex align-items-center">
<img src="{% static 'img/logo/Agdar-Logo.png' %}" alt="" class="me-2" style="height: 128px; ">
{% get_tenant_logo request.user.tenant as tenant_logo %}
{% if tenant_logo %}
<img src="{{ tenant_logo.url }}" alt="{{ request.user.tenant.name }}" style="height: 128px;" class="me-2" />
{% else %}
<img src="{% static 'img/logo/tenhal_logo.png' %}" alt="Tenhal|تنحل" class="me-2" style="height: 128px;" />
{% endif %}
</div>
<small>{% trans "Behavioral Clinic Management System" %}</small>
</div>

View File

@ -1,6 +1,6 @@
{% extends 'base.html' %}
{% load static i18n %}
{% load static i18n tenant_tags %}
{% block title %}{% trans "Sign Up" %}{% endblock %}
@ -13,7 +13,12 @@
<div class="login-header">
<div class="brand">
<div class="d-flex align-items-center">
<img src="{% static 'img/logo/Agdar-Logo.png' %}" alt="" class="me-2" style="height: 128px; ">
{% get_tenant_logo request.user.tenant as tenant_logo %}
{% if tenant_logo %}
<img src="{{ tenant_logo.url }}" alt="{{ request.user.tenant.name }}" style="height: 128px;" class="me-2" />
{% else %}
<img src="{% static 'img/logo/tenhal_logo.png' %}" alt="Tenhal|تنحل" class="me-2" style="height: 128px;" />
{% endif %}
</div>
<small>{% trans "Behavioral Clinic Management System" %}</small>
</div>