update
This commit is contained in:
parent
84c1fb798e
commit
43901b5bda
1
.idea/hospital_management_system_v4.iml
generated
1
.idea/hospital_management_system_v4.iml
generated
@ -19,6 +19,7 @@
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="uv (hospital_management_system_v4)" jdkType="Python SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="toastr.js" level="application" />
|
||||
</component>
|
||||
<component name="PyDocumentationSettings">
|
||||
<option name="format" value="PLAIN" />
|
||||
|
||||
6
.idea/jsLibraryMappings.xml
generated
Normal file
6
.idea/jsLibraryMappings.xml
generated
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="JavaScriptLibraryMappings">
|
||||
<file url="file://$PROJECT_DIR$" libraries="{toastr.js}" />
|
||||
</component>
|
||||
</project>
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -40,6 +40,13 @@ urlpatterns = [
|
||||
path('reschedule/<int:appointment_id>/', views.reschedule_appointment, name='reschedule_appointment'),
|
||||
path('cancel/<int:appointment_id>/', views.cancel_appointment, name='cancel_appointment'),
|
||||
|
||||
path('templates/', views.AppointmentTemplateListView.as_view(), name='appointment_template_list'),
|
||||
path('templates/<int:pk>/', views.AppointmentTemplateDetailView.as_view(), name='appointment_template_detail'),
|
||||
path('templates/create/', views.AppointmentTemplateCreateView.as_view(), name='appointment_template_create'),
|
||||
path('templates/<int:pk>/update/', views.AppointmentTemplateUpdateView.as_view(), name='appointment_template_update'),
|
||||
path('templates/<int:pk>/delete/', views.AppointmentTemplateDeleteView.as_view(), name='appointment_template_delete'),
|
||||
|
||||
|
||||
# API endpoints
|
||||
# path('api/', include('appointments.api.urls')),
|
||||
]
|
||||
|
||||
@ -1002,7 +1002,7 @@ class AppointmentTemplateListView(LoginRequiredMixin, ListView):
|
||||
List appointment templates.
|
||||
"""
|
||||
model = AppointmentTemplate
|
||||
template_name = 'appointments/appointment_template_list.html'
|
||||
template_name = 'appointments/templates/appointment_template_list.html'
|
||||
context_object_name = 'templates'
|
||||
paginate_by = 25
|
||||
|
||||
@ -1066,7 +1066,7 @@ class AppointmentTemplateDetailView(LoginRequiredMixin, DetailView):
|
||||
Display appointment template details.
|
||||
"""
|
||||
model = AppointmentTemplate
|
||||
template_name = 'appointments/appointment_template_detail.html'
|
||||
template_name = 'appointments/templates/appointment_template_detail.html'
|
||||
context_object_name = 'template'
|
||||
|
||||
def get_queryset(self):
|
||||
|
||||
BIN
db.sqlite3
BIN
db.sqlite3
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -58,16 +58,15 @@ class PatientProfileAdmin(admin.ModelAdmin):
|
||||
"""
|
||||
list_display = [
|
||||
'mrn', 'get_full_name', 'date_of_birth', 'age', 'gender',
|
||||
'phone_number', 'is_active',
|
||||
'mobile_number', 'is_active',
|
||||
]
|
||||
list_filter = [
|
||||
'tenant', 'gender', 'race', 'ethnicity', 'marital_status',
|
||||
'tenant', 'gender', 'marital_status',
|
||||
'is_active', 'is_deceased', 'is_vip', 'confidential_patient',
|
||||
|
||||
]
|
||||
search_fields = [
|
||||
'mrn', 'first_name', 'last_name', 'email', 'phone_number',
|
||||
'ssn', 'drivers_license'
|
||||
'mrn', 'id_number', 'first_name', 'last_name', 'email', 'mobile_number',
|
||||
]
|
||||
ordering = ['last_name', 'first_name']
|
||||
|
||||
@ -80,8 +79,7 @@ class PatientProfileAdmin(admin.ModelAdmin):
|
||||
}),
|
||||
('Demographics', {
|
||||
'fields': (
|
||||
'date_of_birth', 'gender', 'sex_assigned_at_birth',
|
||||
'race', 'ethnicity', 'marital_status'
|
||||
'date_of_birth', 'gender', 'marital_status'
|
||||
)
|
||||
}),
|
||||
('Contact Information', {
|
||||
@ -91,9 +89,6 @@ class PatientProfileAdmin(admin.ModelAdmin):
|
||||
'zip_code', 'country'
|
||||
)
|
||||
}),
|
||||
('Identification', {
|
||||
'fields': ('ssn', 'drivers_license', 'drivers_license_state')
|
||||
}),
|
||||
('Language and Communication', {
|
||||
'fields': (
|
||||
'primary_language', 'interpreter_needed', 'communication_preference'
|
||||
|
||||
@ -19,10 +19,9 @@ class PatientProfileForm(forms.ModelForm):
|
||||
model = PatientProfile
|
||||
fields = [
|
||||
'first_name', 'last_name', 'middle_name', 'preferred_name', 'suffix',
|
||||
'date_of_birth', 'gender', 'sex_assigned_at_birth', 'race', 'ethnicity',
|
||||
'phone_number', 'mobile_number', 'email', 'address_line_1', 'address_line_2',
|
||||
'city', 'state', 'zip_code', 'country', 'marital_status', 'occupation',
|
||||
'is_active'
|
||||
'id_number', 'date_of_birth', 'gender', 'phone_number', 'mobile_number',
|
||||
'email', 'address_line_1', 'address_line_2', 'city', 'state', 'zip_code',
|
||||
'country', 'marital_status', 'occupation', 'is_active'
|
||||
]
|
||||
widgets = {
|
||||
'first_name': forms.TextInput(attrs={'class': 'form-control'}),
|
||||
@ -30,11 +29,9 @@ class PatientProfileForm(forms.ModelForm):
|
||||
'middle_name': forms.TextInput(attrs={'class': 'form-control'}),
|
||||
'preferred_name': forms.TextInput(attrs={'class': 'form-control'}),
|
||||
'suffix': forms.TextInput(attrs={'class': 'form-control'}),
|
||||
'id_number': forms.TextInput(attrs={'class': 'form-control'}),
|
||||
'date_of_birth': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}),
|
||||
'gender': forms.Select(attrs={'class': 'form-select'}),
|
||||
'sex_assigned_at_birth': forms.Select(attrs={'class': 'form-select'}),
|
||||
'race': forms.Select(attrs={'class': 'form-select'}),
|
||||
'ethnicity': forms.Select(attrs={'class': 'form-select'}),
|
||||
'phone_number': forms.TextInput(attrs={'class': 'form-control'}),
|
||||
'mobile_number': forms.TextInput(attrs={'class': 'form-control'}),
|
||||
'email': forms.EmailInput(attrs={'class': 'form-control'}),
|
||||
@ -120,7 +117,7 @@ class InsuranceInfoForm(forms.ModelForm):
|
||||
fields = [
|
||||
'insurance_type', 'insurance_company', 'plan_name', 'plan_type',
|
||||
'policy_number', 'group_number', 'subscriber_name', 'subscriber_relationship',
|
||||
'subscriber_dob', 'subscriber_ssn', 'effective_date', 'termination_date',
|
||||
'subscriber_dob', 'subscriber_id_number', 'effective_date', 'termination_date',
|
||||
'copay_amount', 'deductible_amount', 'out_of_pocket_max', 'is_verified',
|
||||
'requires_authorization'
|
||||
]
|
||||
@ -134,7 +131,7 @@ class InsuranceInfoForm(forms.ModelForm):
|
||||
'subscriber_name': forms.TextInput(attrs={'class': 'form-control'}),
|
||||
'subscriber_relationship': forms.Select(attrs={'class': 'form-select'}),
|
||||
'subscriber_dob': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}),
|
||||
'subscriber_ssn': forms.TextInput(attrs={'class': 'form-control'}),
|
||||
'subscriber_id_number': forms.TextInput(attrs={'class': 'form-control'}),
|
||||
'effective_date': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}),
|
||||
'termination_date': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}),
|
||||
'copay_amount': forms.NumberInput(attrs={'class': 'form-control', 'step': '0.01'}),
|
||||
|
||||
@ -0,0 +1,92 @@
|
||||
# Generated by Django 5.2.6 on 2025-09-08 21:04
|
||||
|
||||
import django.core.validators
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("core", "0001_initial"),
|
||||
("patients", "0001_initial"),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveIndex(
|
||||
model_name="patientprofile",
|
||||
name="patients_pa_ssn_07bec5_idx",
|
||||
),
|
||||
migrations.RemoveIndex(
|
||||
model_name="patientprofile",
|
||||
name="patients_pa_phone_n_223ced_idx",
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name="patientprofile",
|
||||
name="drivers_license",
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name="patientprofile",
|
||||
name="drivers_license_state",
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name="patientprofile",
|
||||
name="ethnicity",
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name="patientprofile",
|
||||
name="race",
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name="patientprofile",
|
||||
name="sex_assigned_at_birth",
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name="patientprofile",
|
||||
name="ssn",
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="patientprofile",
|
||||
name="id_number",
|
||||
field=models.CharField(
|
||||
blank=True,
|
||||
help_text="Saudi National ID (10 digits)",
|
||||
max_length=10,
|
||||
null=True,
|
||||
unique=True,
|
||||
validators=[
|
||||
django.core.validators.RegexValidator(
|
||||
message="Saudi National ID must be exactly 10 digits",
|
||||
regex="^\\d{10}$",
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="patientprofile",
|
||||
name="country",
|
||||
field=models.CharField(
|
||||
default="Saudi Arabia", help_text="Country", max_length=100
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="patientprofile",
|
||||
name="primary_language",
|
||||
field=models.CharField(
|
||||
default="Arabic", help_text="Primary language", max_length=50
|
||||
),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name="patientprofile",
|
||||
index=models.Index(
|
||||
fields=["id_number"], name="patients_pa_id_numb_37a581_idx"
|
||||
),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name="patientprofile",
|
||||
index=models.Index(
|
||||
fields=["mobile_number"], name="patients_pa_mobile__f5d849_idx"
|
||||
),
|
||||
),
|
||||
]
|
||||
@ -0,0 +1,35 @@
|
||||
# Generated by Django 5.2.6 on 2025-09-08 21:35
|
||||
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("patients", "0002_remove_patientprofile_patients_pa_ssn_07bec5_idx_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name="insuranceinfo",
|
||||
name="subscriber_ssn",
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="insuranceinfo",
|
||||
name="subscriber_id_number",
|
||||
field=models.CharField(
|
||||
blank=True,
|
||||
help_text="Saudi National ID (10 digits)",
|
||||
max_length=10,
|
||||
null=True,
|
||||
unique=True,
|
||||
validators=[
|
||||
django.core.validators.RegexValidator(
|
||||
message="Saudi National ID must be exactly 10 digits",
|
||||
regex="^\\d{10}$",
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
]
|
||||
Binary file not shown.
Binary file not shown.
@ -21,28 +21,6 @@ class PatientProfile(models.Model):
|
||||
('UNKNOWN', 'Unknown'),
|
||||
('PREFER_NOT_TO_SAY', 'Prefer not to say'),
|
||||
]
|
||||
SEX_ASSIGNED_AT_BIRTH_CHOICES = [
|
||||
('MALE', 'Male'),
|
||||
('FEMALE', 'Female'),
|
||||
('INTERSEX', 'Intersex'),
|
||||
('UNKNOWN', 'Unknown'),
|
||||
]
|
||||
RACE_CHOICES = [
|
||||
('AMERICAN_INDIAN', 'American Indian or Alaska Native'),
|
||||
('ASIAN', 'Asian'),
|
||||
('BLACK', 'Black or African American'),
|
||||
('PACIFIC_ISLANDER', 'Native Hawaiian or Other Pacific Islander'),
|
||||
('WHITE', 'White'),
|
||||
('OTHER', 'Other'),
|
||||
('UNKNOWN', 'Unknown'),
|
||||
('DECLINED', 'Patient Declined'),
|
||||
]
|
||||
ETHNICITY_CHOICES = [
|
||||
('HISPANIC', 'Hispanic or Latino'),
|
||||
('NON_HISPANIC', 'Not Hispanic or Latino'),
|
||||
('UNKNOWN', 'Unknown'),
|
||||
('DECLINED', 'Patient Declined'),
|
||||
]
|
||||
MARITAL_STATUS_CHOICES = [
|
||||
('SINGLE', 'Single'),
|
||||
('MARRIED', 'Married'),
|
||||
@ -128,29 +106,6 @@ class PatientProfile(models.Model):
|
||||
choices=GENDER_CHOICES,
|
||||
help_text='Gender'
|
||||
)
|
||||
sex_assigned_at_birth = models.CharField(
|
||||
max_length=20,
|
||||
choices=SEX_ASSIGNED_AT_BIRTH_CHOICES,
|
||||
blank=True,
|
||||
null=True,
|
||||
help_text='Sex assigned at birth'
|
||||
)
|
||||
|
||||
# Race and Ethnicity
|
||||
race = models.CharField(
|
||||
max_length=50,
|
||||
choices=RACE_CHOICES,
|
||||
blank=True,
|
||||
null=True,
|
||||
help_text='Race'
|
||||
)
|
||||
ethnicity = models.CharField(
|
||||
max_length=50,
|
||||
choices=ETHNICITY_CHOICES,
|
||||
blank=True,
|
||||
null=True,
|
||||
help_text='Ethnicity'
|
||||
)
|
||||
|
||||
# Contact Information
|
||||
email = models.EmailField(
|
||||
@ -212,32 +167,20 @@ class PatientProfile(models.Model):
|
||||
)
|
||||
country = models.CharField(
|
||||
max_length=100,
|
||||
default='United States',
|
||||
default='Saudi Arabia',
|
||||
help_text='Country'
|
||||
)
|
||||
|
||||
# Social Security and Identification
|
||||
ssn = models.CharField(
|
||||
max_length=11,
|
||||
id_number = models.CharField(
|
||||
max_length=10,
|
||||
unique=True,
|
||||
blank=True,
|
||||
null=True,
|
||||
validators=[RegexValidator(
|
||||
regex=r'^\d{3}-\d{2}-\d{4}$',
|
||||
message='SSN must be in format: 123-45-6789'
|
||||
regex=r'^\d{10}$',
|
||||
message='Saudi National ID must be exactly 10 digits'
|
||||
)],
|
||||
help_text='Social Security Number'
|
||||
)
|
||||
drivers_license = models.CharField(
|
||||
max_length=50,
|
||||
blank=True,
|
||||
null=True,
|
||||
help_text='Driver\'s license number'
|
||||
)
|
||||
drivers_license_state = models.CharField(
|
||||
max_length=50,
|
||||
blank=True,
|
||||
null=True,
|
||||
help_text='Driver\'s license state'
|
||||
help_text='Saudi National ID (10 digits)'
|
||||
)
|
||||
|
||||
# Marital Status and Family
|
||||
@ -252,7 +195,7 @@ class PatientProfile(models.Model):
|
||||
# Language and Communication
|
||||
primary_language = models.CharField(
|
||||
max_length=50,
|
||||
default='English',
|
||||
default='Arabic',
|
||||
help_text='Primary language'
|
||||
)
|
||||
interpreter_needed = models.BooleanField(
|
||||
@ -358,7 +301,6 @@ class PatientProfile(models.Model):
|
||||
help_text='User who registered the patient'
|
||||
)
|
||||
|
||||
# Photo
|
||||
photo = models.ImageField(
|
||||
upload_to='patient_photos/',
|
||||
blank=True,
|
||||
@ -366,7 +308,6 @@ class PatientProfile(models.Model):
|
||||
help_text='Patient photo'
|
||||
)
|
||||
|
||||
# Metadata
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
last_visit_date = models.DateTimeField(
|
||||
@ -384,8 +325,8 @@ class PatientProfile(models.Model):
|
||||
models.Index(fields=['tenant', 'mrn']),
|
||||
models.Index(fields=['last_name', 'first_name']),
|
||||
models.Index(fields=['date_of_birth']),
|
||||
models.Index(fields=['ssn']),
|
||||
models.Index(fields=['phone_number']),
|
||||
models.Index(fields=['id_number']),
|
||||
models.Index(fields=['mobile_number']),
|
||||
models.Index(fields=['email']),
|
||||
]
|
||||
unique_together = ['tenant', 'mrn']
|
||||
@ -711,15 +652,16 @@ class InsuranceInfo(models.Model):
|
||||
null=True,
|
||||
help_text='Subscriber date of birth'
|
||||
)
|
||||
subscriber_ssn = models.CharField(
|
||||
max_length=11,
|
||||
subscriber_id_number = models.CharField(
|
||||
max_length=10,
|
||||
unique=True,
|
||||
blank=True,
|
||||
null=True,
|
||||
validators=[RegexValidator(
|
||||
regex=r'^\d{3}-\d{2}-\d{4}$',
|
||||
message='SSN must be in format: 123-45-6789'
|
||||
regex=r'^\d{10}$',
|
||||
message='Saudi National ID must be exactly 10 digits'
|
||||
)],
|
||||
help_text='Subscriber Social Security Number'
|
||||
help_text='Saudi National ID (10 digits)'
|
||||
)
|
||||
|
||||
# Coverage Information
|
||||
|
||||
@ -38,7 +38,7 @@ urlpatterns = [
|
||||
path('patient-search/', views.patient_search, name='patient_search'),
|
||||
path('patient-stats/', views.patient_stats, name='patient_stats'),
|
||||
path('<uuid:patient_id>/emergency-contacts/', views.emergency_contacts_list, name='emergency_contacts_list_api'),
|
||||
path('insurance-info/<int:patient_id>/', views.insurance_info_list, name='insurance_info_list'),
|
||||
path('insurance-info-list/<int:patient_id>/', views.insurance_info_list, name='insurance_info_list'),
|
||||
path('consent-forms/<int:patient_id>/', views.consent_forms_list, name='consent_forms_list'),
|
||||
path('consent-forms/detail/<int:pk>/', views.ConsentFormDetailView.as_view(), name='consent_form_detail'),
|
||||
path('consent-forms/update/<int:pk>/', views.ConsentFormUpdateView.as_view(), name='consent_form_update'),
|
||||
|
||||
@ -35,7 +35,7 @@ class PatientListView(LoginRequiredMixin, ListView):
|
||||
Patient listing view.
|
||||
"""
|
||||
model = PatientProfile
|
||||
template_name = 'patients/patient_list.html'
|
||||
template_name = 'patients/profiles/patient_list.html'
|
||||
context_object_name = 'patients'
|
||||
paginate_by = 25
|
||||
|
||||
@ -610,7 +610,7 @@ class InsuranceInfoCreateView(LoginRequiredMixin, PermissionRequiredMixin, Creat
|
||||
form_class = InsuranceInfoForm
|
||||
template_name = "patients/insurance/insurance_form.html"
|
||||
permission_required = "patients.add_insuranceinfo"
|
||||
success_url = reverse_lazy("patients:insurance_info_list")
|
||||
success_url = reverse_lazy("patients:insurance_list")
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
self.tenant = getattr(request, "tenant", None)
|
||||
|
||||
2
static/js/htmx.min.js
vendored
2
static/js/htmx.min.js
vendored
File diff suppressed because one or more lines are too long
@ -0,0 +1,167 @@
|
||||
{% extends "base.html" %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}Appointment Template - {{ template.name }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<!-- BEGIN breadcrumb -->
|
||||
<ol class="breadcrumb float-xl-end">
|
||||
<li class="breadcrumb-item"><a href="{% url 'core:dashboard' %}">Dashboard</a></li>
|
||||
<li class="breadcrumb-item"><a href="{% url 'appointments:dashboard' %}">Appointments</a></li>
|
||||
<li class="breadcrumb-item"><a href="{% url 'appointments:appointment_template_list' %}">Templates</a></li>
|
||||
<li class="breadcrumb-item active">{{ template.name }}</li>
|
||||
</ol>
|
||||
<!-- END breadcrumb -->
|
||||
|
||||
<!-- BEGIN page-header -->
|
||||
<h1 class="page-header">
|
||||
Appointment Template: {{ template.name }}
|
||||
<small>{{ template.specialty }}</small>
|
||||
</h1>
|
||||
<!-- END page-header -->
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xl-8">
|
||||
<!-- General Info -->
|
||||
<div class="panel panel-inverse mb-3">
|
||||
<div class="panel-heading">
|
||||
<h4 class="panel-title">General Information</h4>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<dl class="row mb-0">
|
||||
<dt class="col-sm-4">Name</dt>
|
||||
<dd class="col-sm-8">{{ template.name }}</dd>
|
||||
|
||||
<dt class="col-sm-4">Description</dt>
|
||||
<dd class="col-sm-8">{{ template.description|default:"—" }}</dd>
|
||||
|
||||
<dt class="col-sm-4">Appointment Type</dt>
|
||||
<dd class="col-sm-8"><span class="badge bg-secondary">{{ template.appointment_type }}</span></dd>
|
||||
|
||||
<dt class="col-sm-4">Specialty</dt>
|
||||
<dd class="col-sm-8">{{ template.specialty }}</dd>
|
||||
|
||||
<dt class="col-sm-4">Duration</dt>
|
||||
<dd class="col-sm-8"><span class="badge bg-primary">{{ template.duration_minutes }} minutes</span></dd>
|
||||
|
||||
<dt class="col-sm-4">Status</dt>
|
||||
<dd class="col-sm-8">
|
||||
{% if template.is_active %}
|
||||
<span class="badge bg-success">Active</span>
|
||||
{% else %}
|
||||
<span class="badge bg-gray-500">Inactive</span>
|
||||
{% endif %}
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Scheduling Rules -->
|
||||
<div class="panel panel-inverse mb-3">
|
||||
<div class="panel-heading">
|
||||
<h4 class="panel-title">Scheduling Rules</h4>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<dl class="row mb-0">
|
||||
<dt class="col-sm-5">Advance Booking Days</dt>
|
||||
<dd class="col-sm-7">{{ template.advance_booking_days }}</dd>
|
||||
|
||||
<dt class="col-sm-5">Minimum Notice (hours)</dt>
|
||||
<dd class="col-sm-7">{{ template.minimum_notice_hours }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Requirements -->
|
||||
<div class="panel panel-inverse mb-3">
|
||||
<div class="panel-heading">
|
||||
<h4 class="panel-title">Requirements</h4>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
{% if template.insurance_verification_required %}
|
||||
<span class="badge bg-info me-1"><i class="fa fa-id-card me-1"></i>Insurance Verification</span>
|
||||
{% endif %}
|
||||
{% if template.authorization_required %}
|
||||
<span class="badge bg-warning text-dark"><i class="fa fa-shield-alt me-1"></i>Authorization Required</span>
|
||||
{% endif %}
|
||||
{% if not template.insurance_verification_required and not template.authorization_required %}
|
||||
<span class="text-muted">No special requirements</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Instructions -->
|
||||
<div class="panel panel-inverse mb-3">
|
||||
<div class="panel-heading">
|
||||
<h4 class="panel-title">Instructions</h4>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<h6>Pre-Appointment Instructions</h6>
|
||||
<p>{{ template.pre_appointment_instructions|linebreaks|default:"—" }}</p>
|
||||
|
||||
<h6 class="mt-3">Post-Appointment Instructions</h6>
|
||||
<p>{{ template.post_appointment_instructions|linebreaks|default:"—" }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Required Forms -->
|
||||
<div class="panel panel-inverse mb-3">
|
||||
<div class="panel-heading">
|
||||
<h4 class="panel-title">Required Forms</h4>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
{% if template.required_forms %}
|
||||
<ul class="mb-0">
|
||||
{% for form in template.required_forms %}
|
||||
<li>{{ form }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<span class="text-muted">No forms required</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sidebar -->
|
||||
<div class="col-xl-4">
|
||||
<!-- Quick Actions -->
|
||||
<div class="panel panel-inverse mb-3">
|
||||
<div class="panel-heading">
|
||||
<h4 class="panel-title">Quick Actions</h4>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="d-grid gap-2">
|
||||
<a href="{% url 'appointments:appointment_template_update' template.pk %}" class="btn btn-primary">
|
||||
<i class="fa fa-edit me-1"></i>Edit
|
||||
</a>
|
||||
<a href="" class="btn btn-secondary">
|
||||
<i class="fa fa-copy me-1"></i>Duplicate
|
||||
</a>
|
||||
{% if template.is_active %}
|
||||
<a href="" class="btn btn-warning">
|
||||
<i class="fa fa-pause-circle me-1"></i>Deactivate
|
||||
</a>
|
||||
{% else %}
|
||||
<a href="" class="btn btn-success">
|
||||
<i class="fa fa-play-circle me-1"></i>Activate
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Metadata -->
|
||||
<div class="panel panel-inverse">
|
||||
<div class="panel-heading">
|
||||
<h4 class="panel-title">Metadata</h4>
|
||||
</div>
|
||||
<div class="panel-body small">
|
||||
<p><strong>Created At:</strong> {{ template.created_at|date:"M d, Y H:i" }}</p>
|
||||
<p><strong>Updated At:</strong> {{ template.updated_at|date:"M d, Y H:i" }}</p>
|
||||
<p><strong>Created By:</strong> {{ template.created_by.get_full_name|default:"—" }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
233
templates/appointments/templates/appointment_template_list.html
Normal file
233
templates/appointments/templates/appointment_template_list.html
Normal file
@ -0,0 +1,233 @@
|
||||
{% extends "base.html" %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}Appointment Templates - Appointments{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<!-- BEGIN breadcrumb -->
|
||||
<ol class="breadcrumb float-xl-end">
|
||||
<li class="breadcrumb-item"><a href="{% url 'core:dashboard' %}">Dashboard</a></li>
|
||||
<li class="breadcrumb-item"><a href="{% url 'appointments:dashboard' %}">Appointments</a></li>
|
||||
<li class="breadcrumb-item active">Appointment Templates</li>
|
||||
</ol>
|
||||
<!-- END breadcrumb -->
|
||||
|
||||
<!-- BEGIN page-header -->
|
||||
<h1 class="page-header">
|
||||
Appointment Templates
|
||||
<small>Reusable configurations for common appointment types</small>
|
||||
</h1>
|
||||
<!-- END page-header -->
|
||||
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xl-12">
|
||||
<div class="panel panel-inverse">
|
||||
<div class="panel-heading">
|
||||
<h4 class="panel-title">Templates</h4>
|
||||
<div class="panel-heading-btn">
|
||||
<a href="{% url 'appointments:appointment_template_create' %}" class="btn btn-xs btn-outline-primary me-2">
|
||||
<i class="fa fa-plus me-1"></i> New Template
|
||||
</a>
|
||||
<a href="javascript:;" class="btn btn-xs btn-icon btn-default" data-toggle="panel-expand"><i class="fa fa-expand"></i></a>
|
||||
<a href="javascript:;" class="btn btn-xs btn-icon btn-success" data-toggle="panel-reload"><i class="fa fa-redo"></i></a>
|
||||
<a href="javascript:;" class="btn btn-xs btn-icon btn-warning" data-toggle="panel-collapse"><i class="fa fa-minus"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel-body">
|
||||
<!-- Toolbar -->
|
||||
<div class="d-flex flex-wrap gap-2 align-items-center mb-3">
|
||||
|
||||
<div class="ms-auto d-flex flex-wrap gap-2">
|
||||
<a href="#" class="btn btn-outline-success"><i class="fa fa-upload me-1"></i> Import</a>
|
||||
<a href="#" class="btn btn-outline-primary"><i class="fa fa-download me-1"></i> Export</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Filters -->
|
||||
<form method="get" class="card card-body mb-3">
|
||||
<div class="row g-2">
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Search</label>
|
||||
<input type="text" name="search" value="{{ request.GET.search|default:'' }}" class="form-control" placeholder="Name, description, department...">
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Appointment Type</label>
|
||||
<select name="appointment_type" class="form-select">
|
||||
<option value="">All types</option>
|
||||
{% for t in appointment_types %}
|
||||
<option value="{{ t }}" {% if request.GET.appointment_type == t %}selected{% endif %}>{{ t }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Department</label>
|
||||
<input type="text" name="department" value="{{ request.GET.department|default:'' }}" class="form-control form-control-sm" placeholder="Department">
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Provider</label>
|
||||
<select name="provider" class="form-select form-select-sm">
|
||||
<option value="">All providers</option>
|
||||
{% for p in providers %}
|
||||
<option value="{{ p.id }}" {% if request.GET.provider == p.id|stringformat:'s' %}selected{% endif %}>
|
||||
{{ p.get_full_name|default:p.username }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Status</label>
|
||||
<select name="status" class="form-select form-select-sm">
|
||||
<option value="">All</option>
|
||||
<option value="active" {% if request.GET.status == 'active' %}selected{% endif %}>Active</option>
|
||||
<option value="inactive" {% if request.GET.status == 'inactive' %}selected{% endif %}>Inactive</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3 align-self-end">
|
||||
<div class="d-flex gap-2">
|
||||
<button type="submit" class="btn btn-sm btn-primary"><i class="fa fa-filter me-1"></i> Filter</button>
|
||||
<a href="{% url 'appointments:appointment_template_list' %}" class="btn btn-sm btn-success">Reset</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<!-- Results -->
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Appointment Type</th>
|
||||
<th>Specialty</th>
|
||||
<th class="text-center">Duration</th>
|
||||
<th>Booking Rules</th>
|
||||
<th>Requirements</th>
|
||||
<th>Status</th>
|
||||
<th class="text-nowrap">Created / Updated</th>
|
||||
<th class="text-end">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for t in templates %}
|
||||
<tr>
|
||||
<td>
|
||||
<div class="fw-semibold">
|
||||
<a href="{% url 'appointments:appointment_template_detail' t.pk %}">{{ t.name }}</a>
|
||||
</div>
|
||||
{% if t.description %}
|
||||
<div class="text-muted small text-truncate" style="max-width: 360px;">{{ t.description }}</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<span class="badge bg-secondary">{{ t.appointment_type }}</span>
|
||||
</td>
|
||||
|
||||
<td>{{ t.specialty }}</td>
|
||||
|
||||
<td class="text-center">
|
||||
<span class="badge bg-primary">{{ t.duration_minutes }}m</span>
|
||||
</td>
|
||||
|
||||
<td class="small">
|
||||
<div><i class="fa fa-calendar me-1 text-muted"></i>Advance: {{ t.advance_booking_days }} days</div>
|
||||
<div><i class="fa fa-bell me-1 text-muted"></i>Notice: {{ t.minimum_notice_hours }} hrs</div>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
{% if t.insurance_verification_required %}
|
||||
<span class="badge bg-info me-1"><i class="fa fa-id-card me-1"></i>Insurance</span>
|
||||
{% endif %}
|
||||
{% if t.authorization_required %}
|
||||
<span class="badge bg-warning text-dark"><i class="fa fa-shield-alt me-1"></i>Auth</span>
|
||||
{% endif %}
|
||||
{% if not t.insurance_verification_required and not t.authorization_required %}
|
||||
<span class="text-muted small">None</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
<td>
|
||||
{% if t.is_active %}
|
||||
<span class="badge bg-success">Active</span>
|
||||
{% else %}
|
||||
<span class="badge bg-gray-500">Inactive</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
<td class="text-nowrap small">
|
||||
<div><i class="fa fa-user me-1 text-muted"></i>{{ t.created_by.get_full_name|default:"—" }}</div>
|
||||
<div class="text-muted">{{ t.updated_at|date:"M d, Y H:i" }}</div>
|
||||
</td>
|
||||
|
||||
<td class="text-end">
|
||||
<div class="btn-group">
|
||||
<a href="{% url 'appointments:appointment_template_detail' t.pk %}" class="btn btn-outline-primary btn-sm" title="View">
|
||||
<i class="fa fa-eye"></i>
|
||||
</a>
|
||||
<a href="{% url 'appointments:appointment_template_update' t.pk %}" class="btn btn-outline-secondary btn-sm" title="Edit">
|
||||
<i class="fa fa-edit"></i>
|
||||
</a>
|
||||
<button class="btn btn-secondary btn-sm dropdown-toggle" data-bs-toggle="dropdown" title="More">
|
||||
<i class="fas fa-ellipsis-v"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-end">
|
||||
|
||||
<li>
|
||||
<a class="dropdown-item" href="">
|
||||
<i class="fa fa-copy me-2"></i>Duplicate
|
||||
</a>
|
||||
</li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
{% if t.is_active %}
|
||||
<li>
|
||||
<a class="dropdown-item text-warning" href="">
|
||||
<i class="fa fa-pause-circle me-2"></i>Deactivate
|
||||
</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li>
|
||||
<a class="dropdown-item text-success" href="">
|
||||
<i class="fa fa-play-circle me-2"></i>Activate
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="9" class="text-center py-5">
|
||||
<div class="text-muted">
|
||||
<i class="fa fa-clipboard-list fa-2x mb-2"></i>
|
||||
<div>No templates found.</div>
|
||||
<div class="mt-2">
|
||||
<a href="{% url 'appointments:appointment_template_create' %}" class="btn btn-primary btn-sm">
|
||||
<i class="fa fa-plus me-1"></i>Create Template
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Pagination -->
|
||||
{% if is_paginated %}
|
||||
{% include 'partial/pagination.html' %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@ -1,5 +1,3 @@
|
||||
|
||||
|
||||
{% load i18n static%}
|
||||
<!DOCTYPE html>
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
@ -19,22 +17,9 @@
|
||||
|
||||
<link href="{% static 'css/vendor.min.css' %}" rel="stylesheet" />
|
||||
<link href="{% static 'css/default/app.min.css' %}" rel="stylesheet" />
|
||||
{# <link href="{% static 'css/apple/app.min.css' %}" rel="stylesheet" />#}
|
||||
{# <link href="{% static 'css/transparent/app.min.css' %}" rel="stylesheet" />#}
|
||||
<link href="{% static 'css/custom.css' %}" rel="stylesheet" />
|
||||
<head>
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.css" rel="stylesheet"/>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Your page content -->
|
||||
{# <link href="{% static 'css/custom.css' %}" rel="stylesheet" />#}
|
||||
|
||||
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/htmx.org@2.0.6/dist/htmx.min.js"></script>
|
||||
</body>
|
||||
{# <script src="{% static 'plugins/apexcharts/dist/apexcharts.min.js' %}"></script>#}
|
||||
<!-- HTMX -->
|
||||
<script src="{% static 'js/htmx.min.js' %}"></script>
|
||||
<!-- ================== END core-css ================== -->
|
||||
|
||||
{% block css %}
|
||||
@ -168,7 +153,7 @@
|
||||
</main>
|
||||
|
||||
|
||||
<div hx-boost="false">
|
||||
<div>
|
||||
{% block content %}
|
||||
|
||||
{% endblock %}
|
||||
@ -184,72 +169,18 @@
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
||||
<!-- ================== BEGIN core-js ================== -->
|
||||
<script src="{% static 'js/htmx.min.js' %}"></script>
|
||||
<script src="{% static 'js/vendor.min.js' %}"></script>
|
||||
<script src="{% static 'js/app.min.js' %}"></script>
|
||||
{#<script src="{% static 'js/custom.js' %}"></script>#}
|
||||
|
||||
|
||||
<!-- ================== END core-js ================== -->
|
||||
|
||||
{% block js %}
|
||||
|
||||
<script>
|
||||
// HTMX configuration
|
||||
htmx.config.globalViewTransitions = true;
|
||||
|
||||
// Auto-refresh indicators
|
||||
document.addEventListener('htmx:beforeRequest', function(evt) {
|
||||
if (evt.detail.target.classList.contains('auto-refresh')) {
|
||||
evt.detail.target.style.opacity = '0.7';
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('htmx:afterRequest', function(evt) {
|
||||
if (evt.detail.target.classList.contains('auto-refresh')) {
|
||||
evt.detail.target.style.opacity = '1';
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
{#// Define a default palette if no colors provided#}
|
||||
{#const defaultPalette = ['#FF6384','#36A2EB','#FFCE56','#4BC0C0','#9966FF','#FF9F40'];#}
|
||||
{##}
|
||||
{#// Function to (re)create all charts#}
|
||||
{#function recreateAllChartsByClass() {#}
|
||||
{# document.querySelectorAll('.apex-chart').forEach(el => {#}
|
||||
{# const optionsText = el.getAttribute('data-options');#}
|
||||
{# if (!optionsText) return;#}
|
||||
{##}
|
||||
{# let options;#}
|
||||
{# try {#}
|
||||
{# options = JSON.parse(optionsText);#}
|
||||
{# } catch (e) {#}
|
||||
{# console.error('❌ Invalid JSON in data-options for', el, e);#}
|
||||
{# return;#}
|
||||
{# }#}
|
||||
{##}
|
||||
{# if (!options.colors) {#}
|
||||
{# options.colors = defaultPalette;#}
|
||||
{# }#}
|
||||
{##}
|
||||
{# if (el._apexInstance && typeof el._apexInstance.destroy === 'function') {#}
|
||||
{# el._apexInstance.destroy();#}
|
||||
{# }#}
|
||||
{##}
|
||||
{# const chart = new ApexCharts(el, options);#}
|
||||
{# chart.render();#}
|
||||
{# el._apexInstance = chart;#}
|
||||
{# });#}
|
||||
{# }#}
|
||||
{##}
|
||||
{#window.addEventListener('load', function() {#}
|
||||
{# setTimeout(recreateAllChartsByClass, 10);#}
|
||||
{# });#}
|
||||
|
||||
/*$(document).on('sortstop', function() {
|
||||
recreateAllChartsByClass();
|
||||
});
|
||||
*/
|
||||
</script>
|
||||
{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
@ -3,9 +3,9 @@
|
||||
|
||||
{% block title %}Patients Dashboard{% endblock %}
|
||||
|
||||
{% block extra_css %}
|
||||
<link href="{% static 'assets/plugins/datatables.net-bs5/css/dataTables.bootstrap5.min.css' %}" rel="stylesheet" />
|
||||
<link href="{% static 'assets/plugins/datatables.net-responsive-bs5/css/responsive.bootstrap5.min.css' %}" rel="stylesheet" />
|
||||
{% block css %}
|
||||
<link href="{% static 'plugins/datatables.net-bs5/css/dataTables.bootstrap5.min.css' %}" rel="stylesheet" />
|
||||
<link href="{% static 'plugins/datatables.net-responsive-bs5/css/responsive.bootstrap5.min.css' %}" rel="stylesheet" />
|
||||
<style>
|
||||
.dashboard-header {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
@ -830,10 +830,10 @@
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
<script src="{% static 'assets/plugins/chart.js/chart.min.js' %}"></script>
|
||||
<script src="{% static 'assets/plugins/datatables.net/js/jquery.dataTables.min.js' %}"></script>
|
||||
<script src="{% static 'assets/plugins/datatables.net-bs5/js/dataTables.bootstrap5.min.js' %}"></script>
|
||||
{% block js %}
|
||||
<script src="{% static 'plugins/chart.js/dist/chart.js' %}"></script>
|
||||
<script src="{% static 'plugins/datatables.net/js/dataTables.min.js' %}"></script>
|
||||
<script src="{% static 'plugins/datatables.net-bs5/js/dataTables.bootstrap5.min.js' %}"></script>
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
|
||||
@ -4,596 +4,408 @@
|
||||
{% block title %}{% if object %}Edit{% else %}Add{% endif %} Insurance - Patients{% endblock %}
|
||||
|
||||
{% block css %}
|
||||
<link href="{% static 'assets/plugins/bootstrap-datepicker/dist/css/bootstrap-datepicker.min.css' %}" rel="stylesheet" />
|
||||
<link href="{% static 'plugins/bootstrap-datepicker/dist/css/bootstrap-datepicker.min.css' %}" rel="stylesheet" />
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<!-- BEGIN breadcrumb -->
|
||||
<ol class="breadcrumb float-xl-end">
|
||||
<li class="breadcrumb-item"><a href="{% url 'core:dashboard' %}">Dashboard</a></li>
|
||||
<li class="breadcrumb-item"><a href="{% url 'patients:patient_list' %}">Patients</a></li>
|
||||
{% if object %}
|
||||
<li class="breadcrumb-item"><a href="{% url 'patients:patient_detail' object.patient.pk %}">{{ object.patient.get_full_name }}</a></li>
|
||||
<li class="breadcrumb-item"><a href="{% url 'patients:insurance_detail' object.pk %}">{{ object.insurance_provider }}</a></li>
|
||||
<li class="breadcrumb-item active">Edit</li>
|
||||
{% else %}
|
||||
<li class="breadcrumb-item"><a href="{% url 'patients:insurance_list' %}">Insurance</a></li>
|
||||
<li class="breadcrumb-item active">Add Insurance</li>
|
||||
<li class="breadcrumb-item"><a href="{% url 'core:dashboard' %}">Dashboard</a></li>
|
||||
<li class="breadcrumb-item"><a href="{% url 'patients:patient_list' %}">Patients</a></li>
|
||||
|
||||
{% if object %}
|
||||
<li class="breadcrumb-item">
|
||||
<a href="{% url 'patients:patient_detail' object.patient.pk %}">{{ object.patient.get_full_name }}</a>
|
||||
</li>
|
||||
<li class="breadcrumb-item active">Edit Insurance</li>
|
||||
{% else %}
|
||||
{% if patient %}
|
||||
<li class="breadcrumb-item">
|
||||
<a href="{% url 'patients:patient_detail' patient.pk %}">{{ patient.get_full_name }}</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
<li class="breadcrumb-item active">Add Insurance</li>
|
||||
{% endif %}
|
||||
</ol>
|
||||
<!-- END breadcrumb -->
|
||||
|
||||
<!-- BEGIN page-header -->
|
||||
<h1 class="page-header">
|
||||
{% if object %}Edit Insurance{% else %}Add Insurance{% endif %}
|
||||
<small>{% if object %}{{ object.insurance_provider }}{% else %}New Insurance Policy{% endif %}</small>
|
||||
{% if object %}Edit Insurance{% else %}Add Insurance{% endif %}
|
||||
<small>
|
||||
{% if object %}{{ object.insurance_company }}{% elif patient %}for {{ patient.get_full_name }}{% else %}New Insurance Policy{% endif %}
|
||||
</small>
|
||||
</h1>
|
||||
<!-- END page-header -->
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xl-8">
|
||||
<!-- BEGIN panel -->
|
||||
<div class="panel panel-inverse">
|
||||
<div class="panel-heading">
|
||||
<h4 class="panel-title">Insurance Information</h4>
|
||||
<div class="panel-heading-btn">
|
||||
<button type="button" class="btn btn-xs btn-info me-2" onclick="saveDraft()">
|
||||
<i class="fa fa-save"></i> Save Draft
|
||||
</button>
|
||||
<a href="javascript:;" class="btn btn-xs btn-icon btn-default" data-toggle="panel-expand"><i class="fa fa-expand"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<form method="post" id="insurance-form" novalidate>
|
||||
{% csrf_token %}
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-6">
|
||||
<div class="form-floating">
|
||||
{{ form.patient }}
|
||||
<label for="{{ form.patient.id_for_label }}">Patient <span class="text-danger">*</span></label>
|
||||
{% if form.patient.errors %}
|
||||
<div class="invalid-feedback d-block">{{ form.patient.errors.0 }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-floating">
|
||||
{{ form.insurance_type }}
|
||||
<label for="{{ form.insurance_type.id_for_label }}">Insurance Type <span class="text-danger">*</span></label>
|
||||
{% if form.insurance_type.errors %}
|
||||
<div class="invalid-feedback d-block">{{ form.insurance_type.errors.0 }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-6">
|
||||
<div class="form-floating">
|
||||
{{ form.insurance_provider }}
|
||||
<label for="{{ form.insurance_provider.id_for_label }}">Insurance Provider <span class="text-danger">*</span></label>
|
||||
{% if form.insurance_provider.errors %}
|
||||
<div class="invalid-feedback d-block">{{ form.insurance_provider.errors.0 }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-floating">
|
||||
{{ form.policy_number }}
|
||||
<label for="{{ form.policy_number.id_for_label }}">Policy Number <span class="text-danger">*</span></label>
|
||||
{% if form.policy_number.errors %}
|
||||
<div class="invalid-feedback d-block">{{ form.policy_number.errors.0 }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-6">
|
||||
<div class="form-floating">
|
||||
{{ form.member_id }}
|
||||
<label for="{{ form.member_id.id_for_label }}">Member ID</label>
|
||||
{% if form.member_id.errors %}
|
||||
<div class="invalid-feedback d-block">{{ form.member_id.errors.0 }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-floating">
|
||||
{{ form.group_number }}
|
||||
<label for="{{ form.group_number.id_for_label }}">Group Number</label>
|
||||
{% if form.group_number.errors %}
|
||||
<div class="invalid-feedback d-block">{{ form.group_number.errors.0 }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-6">
|
||||
<div class="form-floating">
|
||||
{{ form.effective_date }}
|
||||
<label for="{{ form.effective_date.id_for_label }}">Effective Date</label>
|
||||
{% if form.effective_date.errors %}
|
||||
<div class="invalid-feedback d-block">{{ form.effective_date.errors.0 }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-floating">
|
||||
{{ form.expiration_date }}
|
||||
<label for="{{ form.expiration_date.id_for_label }}">Expiration Date</label>
|
||||
{% if form.expiration_date.errors %}
|
||||
<div class="invalid-feedback d-block">{{ form.expiration_date.errors.0 }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-6">
|
||||
<div class="form-floating">
|
||||
{{ form.copay }}
|
||||
<label for="{{ form.copay.id_for_label }}">Copay Amount ($)</label>
|
||||
{% if form.copay.errors %}
|
||||
<div class="invalid-feedback d-block">{{ form.copay.errors.0 }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-floating">
|
||||
{{ form.deductible }}
|
||||
<label for="{{ form.deductible.id_for_label }}">Deductible Amount ($)</label>
|
||||
{% if form.deductible.errors %}
|
||||
<div class="invalid-feedback d-block">{{ form.deductible.errors.0 }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-6">
|
||||
<div class="form-floating">
|
||||
{{ form.status }}
|
||||
<label for="{{ form.status.id_for_label }}">Status <span class="text-danger">*</span></label>
|
||||
{% if form.status.errors %}
|
||||
<div class="invalid-feedback d-block">{{ form.status.errors.0 }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-check mt-3">
|
||||
{{ form.is_primary }}
|
||||
<label class="form-check-label" for="{{ form.is_primary.id_for_label }}">
|
||||
Primary Insurance
|
||||
</label>
|
||||
{% if form.is_primary.errors %}
|
||||
<div class="invalid-feedback d-block">{{ form.is_primary.errors.0 }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.coverage_details.id_for_label }}" class="form-label">Coverage Details</label>
|
||||
{{ form.coverage_details }}
|
||||
{% if form.coverage_details.errors %}
|
||||
<div class="invalid-feedback d-block">{{ form.coverage_details.errors.0 }}</div>
|
||||
{% endif %}
|
||||
<div class="form-text">Describe what services are covered, limitations, and special requirements.</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.notes.id_for_label }}" class="form-label">Additional Notes</label>
|
||||
{{ form.notes }}
|
||||
{% if form.notes.errors %}
|
||||
<div class="invalid-feedback d-block">{{ form.notes.errors.0 }}</div>
|
||||
{% endif %}
|
||||
<div class="form-text">Any additional information about this insurance policy.</div>
|
||||
</div>
|
||||
|
||||
<!-- Form Actions -->
|
||||
<div class="d-flex justify-content-between">
|
||||
<div>
|
||||
<a href="{% if object %}{% url 'patients:insurance_detail' object.pk %}{% else %}{% url 'patients:insurance_list' %}{% endif %}" class="btn btn-secondary">
|
||||
<i class="fa fa-arrow-left me-2"></i>Cancel
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<button type="button" class="btn btn-info me-2" onclick="saveDraft()">
|
||||
<i class="fa fa-save me-2"></i>Save Draft
|
||||
</button>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fa fa-check me-2"></i>{% if object %}Update{% else %}Create{% endif %} Insurance
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-xl-8">
|
||||
<!-- BEGIN panel -->
|
||||
<div class="panel panel-inverse">
|
||||
<div class="panel-heading">
|
||||
<h4 class="panel-title">Insurance Information</h4>
|
||||
<div class="panel-heading-btn">
|
||||
<a href="javascript:;" class="btn btn-xs btn-icon btn-default" data-toggle="panel-expand"><i class="fa fa-expand"></i></a>
|
||||
<a href="javascript:;" class="btn btn-xs btn-icon btn-success" data-toggle="panel-reload"><i class="fa fa-redo"></i></a>
|
||||
<a href="javascript:;" class="btn btn-xs btn-icon btn-warning" data-toggle="panel-collapse"><i class="fa fa-minus"></i></a>
|
||||
</div>
|
||||
<!-- END panel -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-xl-4">
|
||||
<!-- BEGIN panel -->
|
||||
<div class="panel panel-inverse">
|
||||
<div class="panel-heading">
|
||||
<h4 class="panel-title">Help & Guidelines</h4>
|
||||
<div class="panel-body">
|
||||
{% if messages %}
|
||||
{% for message in messages %}
|
||||
<div class="alert alert-{{ message.tags }} alert-dismissible fade show" role="alert">
|
||||
{{ message }}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="alert alert-info">
|
||||
<h6 class="alert-heading">Insurance Information Tips</h6>
|
||||
<ul class="mb-0 small">
|
||||
<li>Verify all information with the insurance card</li>
|
||||
<li>Primary insurance should be billed first</li>
|
||||
<li>Check effective and expiration dates carefully</li>
|
||||
<li>Include group number if available</li>
|
||||
<li>Document any special coverage requirements</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="card border-primary mb-3">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h6 class="card-title mb-0">Required Fields</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<ul class="list-unstyled mb-0 small">
|
||||
<li><i class="fa fa-check text-success me-2"></i>Patient</li>
|
||||
<li><i class="fa fa-check text-success me-2"></i>Insurance Provider</li>
|
||||
<li><i class="fa fa-check text-success me-2"></i>Policy Number</li>
|
||||
<li><i class="fa fa-check text-success me-2"></i>Insurance Type</li>
|
||||
<li><i class="fa fa-check text-success me-2"></i>Status</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card border-warning mb-3">
|
||||
<div class="card-header bg-warning text-dark">
|
||||
<h6 class="card-title mb-0">Validation Rules</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<ul class="list-unstyled mb-0 small">
|
||||
<li><i class="fa fa-exclamation-triangle text-warning me-2"></i>Only one primary insurance per patient</li>
|
||||
<li><i class="fa fa-exclamation-triangle text-warning me-2"></i>Effective date cannot be in the future</li>
|
||||
<li><i class="fa fa-exclamation-triangle text-warning me-2"></i>Expiration date must be after effective date</li>
|
||||
<li><i class="fa fa-exclamation-triangle text-warning me-2"></i>Policy number must be unique per provider</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card border-success">
|
||||
<div class="card-header bg-success text-white">
|
||||
<h6 class="card-title mb-0">Quick Actions</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="d-grid gap-2">
|
||||
<button type="button" class="btn btn-outline-primary btn-sm" onclick="verifyWithProvider()">
|
||||
<i class="fa fa-check-circle me-2"></i>Verify with Provider
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-info btn-sm" onclick="checkEligibility()">
|
||||
<i class="fa fa-search me-2"></i>Check Eligibility
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-secondary btn-sm" onclick="scanInsuranceCard()">
|
||||
<i class="fa fa-camera me-2"></i>Scan Insurance Card
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- END panel -->
|
||||
|
||||
{% if object %}
|
||||
<!-- BEGIN panel -->
|
||||
<div class="panel panel-inverse">
|
||||
<div class="panel-heading">
|
||||
<h4 class="panel-title">Current Patient</h4>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="text-center mb-3">
|
||||
<div class="w-60px h-60px bg-primary rounded-circle d-flex align-items-center justify-content-center mx-auto mb-2">
|
||||
<i class="fa fa-user text-white"></i>
|
||||
</div>
|
||||
<h6 class="mb-1">{{ object.patient.get_full_name }}</h6>
|
||||
<small class="text-muted">{{ object.patient.patient_id }}</small>
|
||||
</div>
|
||||
|
||||
<table class="table table-borderless table-sm">
|
||||
<tr>
|
||||
<td class="fw-bold" width="60">DOB:</td>
|
||||
<td>{{ object.patient.date_of_birth|date:"M d, Y" }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="fw-bold">Age:</td>
|
||||
<td>{{ object.patient.age }} years</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="fw-bold">Gender:</td>
|
||||
<td>{{ object.patient.get_gender_display }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div class="d-grid">
|
||||
<a href="{% url 'patients:patient_detail' object.patient.pk %}" class="btn btn-outline-primary btn-sm">
|
||||
<i class="fa fa-user me-2"></i>View Patient
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- END panel -->
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
<!-- BEGIN panel -->
|
||||
<div class="panel panel-inverse">
|
||||
<div class="panel-heading">
|
||||
<h4 class="panel-title">Form Status</h4>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div id="form-status">
|
||||
<div class="alert alert-secondary">
|
||||
<i class="fa fa-info-circle me-2"></i>
|
||||
<span id="status-text">Form not saved</span>
|
||||
</div>
|
||||
</div>
|
||||
{% if form.non_field_errors %}
|
||||
<div class="alert alert-danger">
|
||||
{{ form.non_field_errors }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="small text-muted">
|
||||
<div>Last saved: <span id="last-saved">Never</span></div>
|
||||
<div>Auto-save: <span class="text-success">Enabled</span></div>
|
||||
</div>
|
||||
<form method="post" id="insurance-form" novalidate>
|
||||
{% csrf_token %}
|
||||
|
||||
<!-- POLICY BASICS -->
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.insurance_type.id_for_label }}" class="form-label">Insurance Type <span class="text-danger">*</span></label>
|
||||
{{ form.insurance_type }}
|
||||
{% if form.insurance_type.errors %}<div class="invalid-feedback d-block">{{ form.insurance_type.errors.0 }}</div>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- END panel -->
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.insurance_company.id_for_label }}" class="form-label">Insurance Company <span class="text-danger">*</span></label>
|
||||
{{ form.insurance_company }}
|
||||
{% if form.insurance_company.errors %}<div class="invalid-feedback d-block">{{ form.insurance_company.errors.0 }}</div>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.plan_name.id_for_label }}" class="form-label">Plan Name</label>
|
||||
{{ form.plan_name }}
|
||||
{% if form.plan_name.errors %}<div class="invalid-feedback d-block">{{ form.plan_name.errors.0 }}</div>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.plan_type.id_for_label }}" class="form-label">Plan Type</label>
|
||||
{{ form.plan_type }}
|
||||
{% if form.plan_type.errors %}<div class="invalid-feedback d-block">{{ form.plan_type.errors.0 }}</div>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.policy_number.id_for_label }}" class="form-label">Policy Number <span class="text-danger">*</span></label>
|
||||
{{ form.policy_number }}
|
||||
{% if form.policy_number.errors %}<div class="invalid-feedback d-block">{{ form.policy_number.errors.0 }}</div>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.group_number.id_for_label }}" class="form-label">Group Number</label>
|
||||
{{ form.group_number }}
|
||||
{% if form.group_number.errors %}<div class="invalid-feedback d-block">{{ form.group_number.errors.0 }}</div>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- SUBSCRIBER -->
|
||||
<div class="row mb-3">
|
||||
<div class="col-12">
|
||||
<h6 class="border-bottom pb-2">Subscriber</h6>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.subscriber_name.id_for_label }}" class="form-label">Subscriber Name</label>
|
||||
{{ form.subscriber_name }}
|
||||
{% if form.subscriber_name.errors %}<div class="invalid-feedback d-block">{{ form.subscriber_name.errors.0 }}</div>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.subscriber_relationship.id_for_label }}" class="form-label">Relationship</label>
|
||||
{{ form.subscriber_relationship }}
|
||||
{% if form.subscriber_relationship.errors %}<div class="invalid-feedback d-block">{{ form.subscriber_relationship.errors.0 }}</div>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.subscriber_dob.id_for_label }}" class="form-label">Subscriber DOB</label>
|
||||
{{ form.subscriber_dob }}
|
||||
{% if form.subscriber_dob.errors %}<div class="invalid-feedback d-block">{{ form.subscriber_dob.errors.0 }}</div>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.subscriber_id_number.id_for_label }}" class="form-label">Subscriber ID Number</label>
|
||||
{{ form.subscriber_id_number }}
|
||||
{% if form.subscriber_id_number.errors %}<div class="invalid-feedback d-block">{{ form.subscriber_id_number.errors.0 }}</div>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- DATES -->
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.effective_date.id_for_label }}" class="form-label">Effective Date</label>
|
||||
{{ form.effective_date }}
|
||||
{% if form.effective_date.errors %}<div class="invalid-feedback d-block">{{ form.effective_date.errors.0 }}</div>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.termination_date.id_for_label }}" class="form-label">Termination Date</label>
|
||||
{{ form.termination_date }}
|
||||
{% if form.termination_date.errors %}<div class="invalid-feedback d-block">{{ form.termination_date.errors.0 }}</div>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- FINANCIALS -->
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-4">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.copay_amount.id_for_label }}" class="form-label">Copay Amount ($)</label>
|
||||
{{ form.copay_amount }}
|
||||
{% if form.copay_amount.errors %}<div class="invalid-feedback d-block">{{ form.copay_amount.errors.0 }}</div>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.deductible_amount.id_for_label }}" class="form-label">Deductible ($)</label>
|
||||
{{ form.deductible_amount }}
|
||||
{% if form.deductible_amount.errors %}<div class="invalid-feedback d-block">{{ form.deductible_amount.errors.0 }}</div>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.out_of_pocket_max.id_for_label }}" class="form-label">Out-of-Pocket Max ($)</label>
|
||||
{{ form.out_of_pocket_max }}
|
||||
{% if form.out_of_pocket_max.errors %}<div class="invalid-feedback d-block">{{ form.out_of_pocket_max.errors.0 }}</div>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- FLAGS -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6">
|
||||
<div class="form-check">
|
||||
{{ form.is_verified }}
|
||||
<label class="form-check-label ms-1" for="{{ form.is_verified.id_for_label }}">Verified</label>
|
||||
{% if form.is_verified.errors %}<div class="invalid-feedback d-block">{{ form.is_verified.errors.0 }}</div>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-check">
|
||||
{{ form.requires_authorization }}
|
||||
<label class="form-check-label ms-1" for="{{ form.requires_authorization.id_for_label }}">Requires Authorization</label>
|
||||
{% if form.requires_authorization.errors %}<div class="invalid-feedback d-block">{{ form.requires_authorization.errors.0 }}</div>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ACTIONS -->
|
||||
<div class="d-flex justify-content-between">
|
||||
<div>
|
||||
<a href="{% url 'patients:insurance_info_list' patient.pk %}" class="btn btn-secondary">
|
||||
<i class="fa fa-arrow-left me-2"></i>Cancel
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fa fa-check me-2"></i>{% if object %}Update{% else %}Create{% endif %} Insurance
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<!-- END panel -->
|
||||
</div>
|
||||
|
||||
<div class="col-xl-4">
|
||||
<!-- PATIENT CARD -->
|
||||
<div class="panel panel-inverse">
|
||||
<div class="panel-heading">
|
||||
<h4 class="panel-title">Patient</h4>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
{% with p=object.patient|default:patient %}
|
||||
{% if p %}
|
||||
<div class="text-center mb-3">
|
||||
<div class="w-60px h-60px bg-primary rounded-circle d-flex align-items-center justify-content-center mx-auto mb-2">
|
||||
<i class="fa fa-user text-white"></i>
|
||||
</div>
|
||||
<h6 class="mb-1">{{ p.get_full_name }}</h6>
|
||||
<small class="text-muted">{{ p.patient_id }}</small>
|
||||
</div>
|
||||
|
||||
<table class="table table-borderless table-sm">
|
||||
<tr><td class="fw-bold" width="60">DOB:</td><td>{{ p.date_of_birth|date:"M d, Y" }}</td></tr>
|
||||
<tr><td class="fw-bold">Age:</td><td>{{ p.age }} years</td></tr>
|
||||
<tr><td class="fw-bold">Gender:</td><td>{{ p.get_gender_display }}</td></tr>
|
||||
</table>
|
||||
|
||||
<div class="d-grid">
|
||||
<a href="{% url 'patients:patient_detail' p.pk %}" class="btn btn-outline-primary btn-sm">
|
||||
<i class="fa fa-user me-2"></i>View Patient
|
||||
</a>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-warning mb-0">No patient context found.</div>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- HELP -->
|
||||
<div class="panel panel-inverse">
|
||||
<div class="panel-heading">
|
||||
<h4 class="panel-title">Help & Guidelines</h4>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="alert alert-info">
|
||||
<h6 class="alert-heading">Tips</h6>
|
||||
<ul class="mb-0 small">
|
||||
<li>Verify against the insurance card.</li>
|
||||
<li>Check effective and termination dates.</li>
|
||||
<li>Record subscriber info if different from patient.</li>
|
||||
<li>Mark “Requires Authorization” when pre-auth is needed.</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="card border-primary mb-3">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h6 class="card-title mb-0">Frequently Required</h6>
|
||||
</div>
|
||||
<div class="card-body small">
|
||||
<ul class="list-unstyled mb-0">
|
||||
<li><i class="fa fa-check text-success me-2"></i>Insurance Type</li>
|
||||
<li><i class="fa fa-check text-success me-2"></i>Insurance Company</li>
|
||||
<li><i class="fa fa-check text-success me-2"></i>Policy Number</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card border-warning">
|
||||
<div class="card-header bg-warning text-dark">
|
||||
<h6 class="card-title mb-0">Validation Rules</h6>
|
||||
</div>
|
||||
<div class="card-body small">
|
||||
<ul class="list-unstyled mb-0">
|
||||
<li><i class="fa fa-exclamation-triangle text-warning me-2"></i>Termination date must be after effective date.</li>
|
||||
<li><i class="fa fa-exclamation-triangle text-warning me-2"></i>Policy number often must be unique by company.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Verification Modal -->
|
||||
<!-- Verification Modal (hooks kept for later integrations) -->
|
||||
<div class="modal fade" id="verificationModal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Verify Insurance</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div id="verification-results">
|
||||
<!-- Verification results will be loaded here -->
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Verify Insurance</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div id="verification-results"></div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
<script src="{% static 'assets/plugins/bootstrap-datepicker/dist/js/bootstrap-datepicker.min.js' %}"></script>
|
||||
|
||||
<script src="{% static 'plugins/bootstrap-datepicker/dist/js/bootstrap-datepicker.min.js' %}"></script>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
// Initialize date pickers
|
||||
$('#{{ form.effective_date.id_for_label }}, #{{ form.expiration_date.id_for_label }}').datepicker({
|
||||
format: 'yyyy-mm-dd',
|
||||
autoclose: true,
|
||||
todayHighlight: true
|
||||
});
|
||||
$(function () {
|
||||
// Datepickers (optional; widgets already type="date")
|
||||
$('#{{ form.effective_date.id_for_label }}, #{{ form.termination_date.id_for_label }}').datepicker({
|
||||
format: 'yyyy-mm-dd',
|
||||
autoclose: true,
|
||||
todayHighlight: true
|
||||
});
|
||||
|
||||
// Form validation
|
||||
$('#insurance-form').on('submit', function(e) {
|
||||
if (!validateForm()) {
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
// Auto-save functionality
|
||||
var autoSaveTimer;
|
||||
$('#insurance-form input, #insurance-form select, #insurance-form textarea').on('change input', function() {
|
||||
clearTimeout(autoSaveTimer);
|
||||
autoSaveTimer = setTimeout(function() {
|
||||
saveDraft();
|
||||
}, 5000); // Auto-save after 5 seconds of inactivity
|
||||
});
|
||||
|
||||
// Insurance type change handler
|
||||
$('#{{ form.insurance_type.id_for_label }}').on('change', function() {
|
||||
var type = $(this).val();
|
||||
if (type === 'PRIMARY') {
|
||||
$('#{{ form.is_primary.id_for_label }}').prop('checked', true);
|
||||
checkPrimaryInsurance();
|
||||
}
|
||||
});
|
||||
|
||||
// Primary insurance checkbox handler
|
||||
$('#{{ form.is_primary.id_for_label }}').on('change', function() {
|
||||
if ($(this).is(':checked')) {
|
||||
checkPrimaryInsurance();
|
||||
}
|
||||
});
|
||||
|
||||
// Policy number validation
|
||||
$('#{{ form.policy_number.id_for_label }}').on('blur', function() {
|
||||
validatePolicyNumber();
|
||||
});
|
||||
|
||||
// Date validation
|
||||
$('#{{ form.effective_date.id_for_label }}, #{{ form.expiration_date.id_for_label }}').on('change', function() {
|
||||
validateDates();
|
||||
});
|
||||
});
|
||||
|
||||
function validateForm() {
|
||||
var isValid = true;
|
||||
var errors = [];
|
||||
|
||||
// Required field validation
|
||||
var requiredFields = ['{{ form.patient.id_for_label }}', '{{ form.insurance_provider.id_for_label }}', '{{ form.policy_number.id_for_label }}', '{{ form.insurance_type.id_for_label }}', '{{ form.status.id_for_label }}'];
|
||||
|
||||
requiredFields.forEach(function(fieldId) {
|
||||
var field = $('#' + fieldId);
|
||||
if (!field.val()) {
|
||||
field.addClass('is-invalid');
|
||||
errors.push(field.closest('.form-floating').find('label').text().replace(' *', '') + ' is required');
|
||||
isValid = false;
|
||||
} else {
|
||||
field.removeClass('is-invalid');
|
||||
}
|
||||
});
|
||||
|
||||
// Date validation
|
||||
if (!validateDates()) {
|
||||
isValid = false;
|
||||
// Client-side sanity check for dates
|
||||
function validateDates() {
|
||||
const eff = $('#{{ form.effective_date.id_for_label }}').val();
|
||||
const term = $('#{{ form.termination_date.id_for_label }}').val();
|
||||
if (eff && term) {
|
||||
const e = new Date(eff);
|
||||
const t = new Date(term);
|
||||
if (t <= e) {
|
||||
$('#{{ form.termination_date.id_for_label }}').addClass('is-invalid');
|
||||
return false;
|
||||
} else {
|
||||
$('#{{ form.termination_date.id_for_label }}').removeClass('is-invalid');
|
||||
}
|
||||
}
|
||||
|
||||
if (!isValid) {
|
||||
toastr.error('Please fix the following errors:\n' + errors.join('\n'));
|
||||
}
|
||||
|
||||
return isValid;
|
||||
}
|
||||
|
||||
function validateDates() {
|
||||
var effectiveDate = $('#{{ form.effective_date.id_for_label }}').val();
|
||||
var expirationDate = $('#{{ form.expiration_date.id_for_label }}').val();
|
||||
|
||||
if (effectiveDate && expirationDate) {
|
||||
var effective = new Date(effectiveDate);
|
||||
var expiration = new Date(expirationDate);
|
||||
|
||||
if (expiration <= effective) {
|
||||
$('#{{ form.expiration_date.id_for_label }}').addClass('is-invalid');
|
||||
toastr.error('Expiration date must be after effective date');
|
||||
return false;
|
||||
} else {
|
||||
$('#{{ form.expiration_date.id_for_label }}').removeClass('is-invalid');
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function checkPrimaryInsurance() {
|
||||
var patientId = $('#{{ form.patient.id_for_label }}').val();
|
||||
if (!patientId) return;
|
||||
$('#{{ form.effective_date.id_for_label }}, #{{ form.termination_date.id_for_label }}').on('change', validateDates);
|
||||
|
||||
$.ajax({
|
||||
url: '{% url "patients:check_primary_insurance" %}',
|
||||
method: 'POST',
|
||||
data: {
|
||||
'csrfmiddlewaretoken': '{{ csrf_token }}',
|
||||
'patient_id': patientId,
|
||||
'current_insurance_id': {% if object %}'{{ object.pk }}'{% else %}null{% endif %}
|
||||
},
|
||||
success: function(response) {
|
||||
if (response.has_primary) {
|
||||
toastr.warning('Patient already has a primary insurance. This will replace the existing primary insurance.');
|
||||
}
|
||||
}
|
||||
// Minimal required validation on submit (server remains source of truth)
|
||||
$('#insurance-form').on('submit', function (e) {
|
||||
let ok = true;
|
||||
const requiredIds = [
|
||||
'{{ form.insurance_type.id_for_label }}',
|
||||
'{{ form.insurance_company.id_for_label }}',
|
||||
'{{ form.policy_number.id_for_label }}'
|
||||
];
|
||||
requiredIds.forEach(function(id){
|
||||
const $f = $('#'+id);
|
||||
if(!$f.val()){
|
||||
$f.addClass('is-invalid');
|
||||
ok = false;
|
||||
} else {
|
||||
$f.removeClass('is-invalid');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function validatePolicyNumber() {
|
||||
var policyNumber = $('#{{ form.policy_number.id_for_label }}').val();
|
||||
var provider = $('#{{ form.insurance_provider.id_for_label }}').val();
|
||||
|
||||
if (policyNumber && provider) {
|
||||
$.ajax({
|
||||
url: '{% url "patients:validate_policy_number" %}',
|
||||
method: 'POST',
|
||||
data: {
|
||||
'csrfmiddlewaretoken': '{{ csrf_token }}',
|
||||
'policy_number': policyNumber,
|
||||
'provider': provider,
|
||||
'current_insurance_id': {% if object %}'{{ object.pk }}'{% else %}null{% endif %}
|
||||
},
|
||||
success: function(response) {
|
||||
if (!response.is_valid) {
|
||||
$('#{{ form.policy_number.id_for_label }}').addClass('is-invalid');
|
||||
toastr.error('Policy number already exists for this provider');
|
||||
} else {
|
||||
$('#{{ form.policy_number.id_for_label }}').removeClass('is-invalid');
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!validateDates()) ok = false;
|
||||
if (!ok) {
|
||||
e.preventDefault();
|
||||
alert('Please fill required fields and fix highlighted errors.');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function saveDraft() {
|
||||
var formData = $('#insurance-form').serialize();
|
||||
|
||||
$.ajax({
|
||||
url: '{% url "patients:save_insurance_draft" %}',
|
||||
method: 'POST',
|
||||
data: formData,
|
||||
success: function(response) {
|
||||
updateFormStatus('Draft saved', 'success');
|
||||
$('#last-saved').text(new Date().toLocaleTimeString());
|
||||
},
|
||||
error: function() {
|
||||
updateFormStatus('Failed to save draft', 'danger');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updateFormStatus(message, type) {
|
||||
var alertClass = 'alert-' + type;
|
||||
$('#form-status').html('<div class="alert ' + alertClass + '"><i class="fa fa-info-circle me-2"></i>' + message + '</div>');
|
||||
}
|
||||
|
||||
function verifyWithProvider() {
|
||||
var provider = $('#{{ form.insurance_provider.id_for_label }}').val();
|
||||
var policyNumber = $('#{{ form.policy_number.id_for_label }}').val();
|
||||
|
||||
if (!provider || !policyNumber) {
|
||||
toastr.error('Please enter insurance provider and policy number first');
|
||||
return;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: '{% url "patients:verify_with_provider" %}',
|
||||
method: 'POST',
|
||||
data: {
|
||||
'csrfmiddlewaretoken': '{{ csrf_token }}',
|
||||
'provider': provider,
|
||||
'policy_number': policyNumber
|
||||
},
|
||||
beforeSend: function() {
|
||||
$('#verification-results').html('<div class="text-center"><i class="fa fa-spinner fa-spin fa-2x"></i><br>Verifying with provider...</div>');
|
||||
$('#verificationModal').modal('show');
|
||||
},
|
||||
success: function(response) {
|
||||
$('#verification-results').html(response.html);
|
||||
},
|
||||
error: function() {
|
||||
$('#verification-results').html('<div class="alert alert-danger">Failed to verify with provider. Please try again.</div>');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
{#function checkEligibility() {#}
|
||||
{# var formData = $('#insurance-form').serialize();#}
|
||||
{# #}
|
||||
{# $.ajax({#}
|
||||
{# url: '{% url "patients:check_eligibility" pk %}',#}
|
||||
{# method: 'POST',#}
|
||||
{# data: formData,#}
|
||||
{# beforeSend: function() {#}
|
||||
{# $('#verification-results').html('<div class="text-center"><i class="fa fa-spinner fa-spin fa-2x"></i><br>Checking eligibility...</div>');#}
|
||||
{# $('#verificationModal').modal('show');#}
|
||||
{# },#}
|
||||
{# success: function(response) {#}
|
||||
{# $('#verification-results').html(response.html);#}
|
||||
{# },#}
|
||||
{# error: function() {#}
|
||||
{# $('#verification-results').html('<div class="alert alert-danger">Failed to check eligibility. Please try again.</div>');#}
|
||||
{# }#}
|
||||
{# });#}
|
||||
{# }#}
|
||||
|
||||
function scanInsuranceCard() {
|
||||
toastr.info('Insurance card scanning feature coming soon!');
|
||||
}
|
||||
// Optional: simple uniqueness hint (adjust endpoint if you add it)
|
||||
$('#{{ form.policy_number.id_for_label }}, #{{ form.insurance_company.id_for_label }}').on('blur', function(){
|
||||
// Placeholder for future AJAX uniqueness check
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
@ -15,8 +15,7 @@
|
||||
<span class="badge bg-success">Verified</span>
|
||||
{% else %}
|
||||
<button class="btn btn-sm btn-outline-primary"
|
||||
hx-post="{% url 'patients:verify_insurance' insurance.id %}"
|
||||
hx-swap="outerHTML">
|
||||
hx-post="{% url 'patients:verify_insurance' insurance.id %}">
|
||||
Verify
|
||||
</button>
|
||||
{% endif %}
|
||||
|
||||
@ -45,9 +45,6 @@
|
||||
</div>
|
||||
<div>{{ patient.get_gender_display }}</div>
|
||||
<div>DOB: {{ patient.date_of_birth|date:"M d, Y" }}</div>
|
||||
{% if patient.race %}
|
||||
<small class="text-muted">{{ patient.get_race_display }}</small>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if patient.email %}
|
||||
@ -73,9 +70,9 @@
|
||||
<div>{{ primary_insurance.insurance_company }}</div>
|
||||
<small class="text-muted">{{ primary_insurance.plan_name }}</small>
|
||||
{% if primary_insurance.is_verified %}
|
||||
<br><span class="badge bg-success">Verified</span>
|
||||
<br><span class="badge bg-success fs-9px">Verified</span>
|
||||
{% else %}
|
||||
<br><span class="badge bg-warning">Unverified</span>
|
||||
<br><span class="badge bg-warning fs-9px">Unverified</span>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<span class="text-muted">No insurance</span>
|
||||
@ -85,23 +82,23 @@
|
||||
<td>
|
||||
<div>
|
||||
{% if patient.is_deceased %}
|
||||
<span class="badge bg-dark">Deceased</span>
|
||||
<span class="badge bg-dark fs-9px">Deceased</span>
|
||||
{% elif patient.is_active %}
|
||||
<span class="badge bg-success">Active</span>
|
||||
<span class="badge bg-success fs-9px">Active</span>
|
||||
{% else %}
|
||||
<span class="badge bg-secondary">Inactive</span>
|
||||
<span class="badge bg-secondary fs-9px">Inactive</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if patient.allergies %}
|
||||
<div class="mt-1">
|
||||
<span class="badge bg-danger" title="Has allergies">
|
||||
<span class="badge bg-danger fs-9px" title="Has allergies">
|
||||
<i class="fas fa-exclamation-triangle"></i> Allergies
|
||||
</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if patient.medical_alerts %}
|
||||
<div class="mt-1">
|
||||
<span class="badge bg-warning text-dark" title="Medical alerts">
|
||||
<span class="badge bg-warning text-dark fs-9px" title="Medical alerts">
|
||||
<i class="fas fa-bell"></i> Alerts
|
||||
</span>
|
||||
</div>
|
||||
@ -129,8 +126,10 @@
|
||||
<button type="button"
|
||||
class="btn btn-outline-info"
|
||||
title="View Insurance"
|
||||
hx-get="{% url 'patients:insurance_info_list' patient.id %}"
|
||||
hx-get="{% url 'patients:insurance_info_list' patient.pk %}"
|
||||
hx-target="#insurance-modal-body"
|
||||
hx-swap="innerHTML"
|
||||
hx-trigger="click"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#insuranceModal">
|
||||
<i class="fas fa-shield-alt"></i>
|
||||
@ -138,12 +137,18 @@
|
||||
<button type="button"
|
||||
class="btn btn-outline-success"
|
||||
title="View Consents"
|
||||
hx-get="{% url 'patients:consent_forms_list' patient.id %}"
|
||||
hx-get="{% url 'patients:consent_forms_list' patient.pk %}"
|
||||
hx-target="#consent-modal-body"
|
||||
hx-swap="innerHTML"
|
||||
hx-trigger="click"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#consentModal">
|
||||
<i class="fas fa-file-signature"></i>
|
||||
</button>
|
||||
<button type="button"
|
||||
class="btn btn-outline-warning"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#quickActionsModal"></button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@ -157,10 +162,11 @@
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- prevents close & backdrop churn -->
|
||||
|
||||
<!-- Insurance Modal -->
|
||||
<div class="modal fade" id="insuranceModal" tabindex="-1" aria-labelledby="insuranceModalLabel" aria-hidden="true">
|
||||
<div class="modal " id="insuranceModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
@ -177,7 +183,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Consent Modal -->
|
||||
<div class="modal fade" id="consentModal" tabindex="-1" aria-labelledby="consentModalLabel" aria-hidden="true">
|
||||
<div class="modal " id="consentModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
@ -193,3 +199,54 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="quickActionsModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">
|
||||
<i class="fas fa-bolt me-2"></i>Quick Actions
|
||||
</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<a href="{% url 'patients:patient_registration' %}" class="btn btn-outline-primary w-100 text-start">
|
||||
<i class="fas fa-user-plus me-3"></i>Register New Patient
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<a href="{% url 'appointments:appointment_create' %}" class="btn btn-outline-success w-100 text-start">
|
||||
<i class="fas fa-calendar-plus me-3"></i>Schedule Appointment
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<a href="" class="btn btn-outline-info w-100 text-start">
|
||||
<i class="fas fa-notes-medical me-3"></i>Create Medical Record
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<a href="{% url 'laboratory:lab_order_create' %}" class="btn btn-outline-warning w-100 text-start">
|
||||
<i class="fas fa-vial me-3"></i>Order Laboratory Test
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<a href="{% url 'billing:bill_create' %}" class="btn btn-outline-danger w-100 text-start">
|
||||
<i class="fas fa-file-invoice-dollar me-3"></i>Create New Bill
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<a href="" class="btn btn-outline-secondary w-100 text-start">
|
||||
<i class="fas fa-prescription-bottle-alt me-3"></i>Write Prescription
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -33,4 +33,3 @@
|
||||
<p>No patient notes</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
|
||||
@ -5,226 +5,247 @@
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h4 class="card-title mb-0">
|
||||
<i class="fas fa-user-plus me-2"></i>Patient Registration
|
||||
</h4>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
|
||||
<div class="card-body">
|
||||
<form id="patient-registration-form" method="post">
|
||||
{% csrf_token %}
|
||||
|
||||
<!-- Personal Information -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<h5 class="border-bottom pb-2">Personal Information</h5>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="mb-3">
|
||||
<label for="first_name" class="form-label">First Name *</label>
|
||||
<input type="text" class="form-control" id="first_name" name="first_name" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="mb-3">
|
||||
<label for="last_name" class="form-label">Last Name *</label>
|
||||
<input type="text" class="form-control" id="last_name" name="last_name" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="mb-3">
|
||||
<label for="middle_name" class="form-label">Middle Name</label>
|
||||
<input type="text" class="form-control" id="middle_name" name="middle_name">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="mb-3">
|
||||
<label for="date_of_birth" class="form-label">Date of Birth *</label>
|
||||
<input type="date" class="form-control" id="date_of_birth" name="date_of_birth" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="mb-3">
|
||||
<label for="gender" class="form-label">Gender *</label>
|
||||
<select class="form-select" id="gender" name="gender" required>
|
||||
<option value="">Select gender...</option>
|
||||
<option value="MALE">Male</option>
|
||||
<option value="FEMALE">Female</option>
|
||||
<option value="OTHER">Other</option>
|
||||
<option value="UNKNOWN">Prefer not to say</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="mb-3">
|
||||
<label for="ssn" class="form-label">SSN</label>
|
||||
<input type="text" class="form-control" id="ssn" name="ssn" placeholder="XXX-XX-XXXX">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Contact Information -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<h5 class="border-bottom pb-2">Contact Information</h5>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="phone_number" class="form-label">Phone Number</label>
|
||||
<input type="tel" class="form-control" id="phone_number" name="phone_number">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="email" class="form-label">Email Address</label>
|
||||
<input type="email" class="form-control" id="email" name="email">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<div class="mb-3">
|
||||
<label for="address" class="form-label">Address</label>
|
||||
<textarea class="form-control" id="address" name="address" rows="2"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Emergency Contact -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<h5 class="border-bottom pb-2">Emergency Contact</h5>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="mb-3">
|
||||
<label for="emergency_name" class="form-label">Full Name *</label>
|
||||
<input type="text" class="form-control" id="emergency_name" name="emergency_name" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="mb-3">
|
||||
<label for="emergency_relationship" class="form-label">Relationship *</label>
|
||||
<select class="form-select" id="emergency_relationship" name="emergency_relationship" required>
|
||||
<option value="">Select relationship...</option>
|
||||
<option value="SPOUSE">Spouse</option>
|
||||
<option value="PARENT">Parent</option>
|
||||
<option value="CHILD">Child</option>
|
||||
<option value="SIBLING">Sibling</option>
|
||||
<option value="FRIEND">Friend</option>
|
||||
<option value="OTHER">Other</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="mb-3">
|
||||
<label for="emergency_phone" class="form-label">Phone Number *</label>
|
||||
<input type="tel" class="form-control" id="emergency_phone" name="emergency_phone" required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Insurance Information -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<h5 class="border-bottom pb-2">Insurance Information</h5>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="insurance_provider" class="form-label">Insurance Provider</label>
|
||||
<input type="text" class="form-control" id="insurance_provider" name="insurance_provider">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="insurance_policy_number" class="form-label">Policy Number</label>
|
||||
<input type="text" class="form-control" id="insurance_policy_number" name="insurance_policy_number">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Consent Forms -->
|
||||
{% if consent_templates %}
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<h5 class="border-bottom pb-2">Consent Forms</h5>
|
||||
{% for template in consent_templates %}
|
||||
<div class="form-check mb-2">
|
||||
<input class="form-check-input" type="checkbox"
|
||||
id="consent_{{ template.id }}"
|
||||
name="consent_templates"
|
||||
value="{{ template.id }}"
|
||||
{% if template.is_required %}required{% endif %}>
|
||||
<label class="form-check-label" for="consent_{{ template.id }}">
|
||||
{{ template.name }}
|
||||
{% if template.is_required %}<span class="text-danger">*</span>{% endif %}
|
||||
</label>
|
||||
{% if template.description %}
|
||||
<div class="form-text">{{ template.description }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Submit Buttons -->
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="d-flex justify-content-end gap-2">
|
||||
<button type="button" class="btn btn-secondary">Cancel</button>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fas fa-save me-1"></i>Register Patient
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-inverse">
|
||||
<div class="panel-heading">
|
||||
<h4 class="panel-title">
|
||||
<i class="fas fa-user-plus me-2"></i>Patient Registration
|
||||
</h4>
|
||||
<div class="panel-heading-btn">
|
||||
<a href="javascript:;" class="btn btn-xs btn-icon btn-default" data-toggle="panel-expand"><i class="fa fa-expand"></i></a>
|
||||
<a href="javascript:;" class="btn btn-xs btn-icon btn-success" data-toggle="panel-reload"><i class="fa fa-redo"></i></a>
|
||||
<a href="javascript:;" class="btn btn-xs btn-icon btn-warning" data-toggle="panel-collapse"><i class="fa fa-minus"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel-body">
|
||||
{% if messages %}
|
||||
{% for message in messages %}
|
||||
<div class="alert alert-{{ message.tags }} alert-dismissible fade show" role="alert">
|
||||
{{ message }}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{% if form.non_field_errors %}
|
||||
<div class="alert alert-danger">
|
||||
{{ form.non_field_errors }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<form id="patient-registration-form" method="post" novalidate>
|
||||
{% csrf_token %}
|
||||
|
||||
<!-- Personal Information -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<h5 class="border-bottom pb-2">Personal Information</h5>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.first_name.id_for_label }}" class="form-label">First Name *</label>
|
||||
{{ form.first_name }}
|
||||
{% if form.first_name.errors %}<div class="invalid-feedback d-block">{{ form.first_name.errors.0 }}</div>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.last_name.id_for_label }}" class="form-label">Last Name *</label>
|
||||
{{ form.last_name }}
|
||||
{% if form.last_name.errors %}<div class="invalid-feedback d-block">{{ form.last_name.errors.0 }}</div>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.middle_name.id_for_label }}" class="form-label">Middle Name</label>
|
||||
{{ form.middle_name }}
|
||||
{% if form.middle_name.errors %}<div class="invalid-feedback d-block">{{ form.middle_name.errors.0 }}</div>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.preferred_name.id_for_label }}" class="form-label">Preferred Name</label>
|
||||
{{ form.preferred_name }}
|
||||
{% if form.preferred_name.errors %}<div class="invalid-feedback d-block">{{ form.preferred_name.errors.0 }}</div>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.suffix.id_for_label }}" class="form-label">Suffix</label>
|
||||
{{ form.suffix }}
|
||||
{% if form.suffix.errors %}<div class="invalid-feedback d-block">{{ form.suffix.errors.0 }}</div>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.id_number.id_for_label }}" class="form-label">ID Number</label>
|
||||
{{ form.id_number }}
|
||||
{% if form.id_number.errors %}<div class="invalid-feedback d-block">{{ form.id_number.errors.0 }}</div>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.date_of_birth.id_for_label }}" class="form-label">Date of Birth *</label>
|
||||
{{ form.date_of_birth }}
|
||||
{% if form.date_of_birth.errors %}<div class="invalid-feedback d-block">{{ form.date_of_birth.errors.0 }}</div>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.gender.id_for_label }}" class="form-label">Gender *</label>
|
||||
{{ form.gender }}
|
||||
{% if form.gender.errors %}<div class="invalid-feedback d-block">{{ form.gender.errors.0 }}</div>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.marital_status.id_for_label }}" class="form-label">Marital Status</label>
|
||||
{{ form.marital_status }}
|
||||
{% if form.marital_status.errors %}<div class="invalid-feedback d-block">{{ form.marital_status.errors.0 }}</div>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Contact Information -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<h5 class="border-bottom pb-2">Contact Information</h5>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.phone_number.id_for_label }}" class="form-label">Phone Number</label>
|
||||
{{ form.phone_number }}
|
||||
{% if form.phone_number.errors %}<div class="invalid-feedback d-block">{{ form.phone_number.errors.0 }}</div>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.mobile_number.id_for_label }}" class="form-label">Mobile Number</label>
|
||||
{{ form.mobile_number }}
|
||||
{% if form.mobile_number.errors %}<div class="invalid-feedback d-block">{{ form.mobile_number.errors.0 }}</div>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.email.id_for_label }}" class="form-label">Email</label>
|
||||
{{ form.email }}
|
||||
{% if form.email.errors %}<div class="invalid-feedback d-block">{{ form.email.errors.0 }}</div>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Address -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<h5 class="border-bottom pb-2">Address</h5>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.address_line_1.id_for_label }}" class="form-label">Address Line 1</label>
|
||||
{{ form.address_line_1 }}
|
||||
{% if form.address_line_1.errors %}<div class="invalid-feedback d-block">{{ form.address_line_1.errors.0 }}</div>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.address_line_2.id_for_label }}" class="form-label">Address Line 2</label>
|
||||
{{ form.address_line_2 }}
|
||||
{% if form.address_line_2.errors %}<div class="invalid-feedback d-block">{{ form.address_line_2.errors.0 }}</div>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.city.id_for_label }}" class="form-label">City</label>
|
||||
{{ form.city }}
|
||||
{% if form.city.errors %}<div class="invalid-feedback d-block">{{ form.city.errors.0 }}</div>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.state.id_for_label }}" class="form-label">State/Province</label>
|
||||
{{ form.state }}
|
||||
{% if form.state.errors %}<div class="invalid-feedback d-block">{{ form.state.errors.0 }}</div>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.zip_code.id_for_label }}" class="form-label">ZIP/Postal Code</label>
|
||||
{{ form.zip_code }}
|
||||
{% if form.zip_code.errors %}<div class="invalid-feedback d-block">{{ form.zip_code.errors.0 }}</div>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.country.id_for_label }}" class="form-label">Country</label>
|
||||
{{ form.country }}
|
||||
{% if form.country.errors %}<div class="invalid-feedback d-block">{{ form.country.errors.0 }}</div>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Other -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<h5 class="border-bottom pb-2">Other</h5>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.occupation.id_for_label }}" class="form-label">Occupation</label>
|
||||
{{ form.occupation }}
|
||||
{% if form.occupation.errors %}<div class="invalid-feedback d-block">{{ form.occupation.errors.0 }}</div>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 d-flex align-items-center">
|
||||
<div class="form-check mb-3">
|
||||
{{ form.is_active }}
|
||||
<label class="form-check-label ms-1" for="{{ form.is_active.id_for_label }}">Active</label>
|
||||
{% if form.is_active.errors %}<div class="invalid-feedback d-block">{{ form.is_active.errors.0 }}</div>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Actions -->
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="d-flex justify-content-end gap-2">
|
||||
<a href="{% url 'patients:patient_list' %}" class="btn btn-secondary">Cancel</a>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fas fa-save me-1"></i>Register Patient
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div> <!-- /panel-body -->
|
||||
</div> <!-- /panel -->
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Form validation and submission
|
||||
document.getElementById('patient-registration-form').addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
// Basic validation
|
||||
const requiredFields = this.querySelectorAll('[required]');
|
||||
let isValid = true;
|
||||
|
||||
requiredFields.forEach(field => {
|
||||
if (!field.value.trim()) {
|
||||
field.classList.add('is-invalid');
|
||||
isValid = false;
|
||||
} else {
|
||||
field.classList.remove('is-invalid');
|
||||
}
|
||||
});
|
||||
|
||||
if (isValid) {
|
||||
// Submit form
|
||||
this.submit();
|
||||
}
|
||||
});
|
||||
|
||||
// SSN formatting
|
||||
document.getElementById('ssn').addEventListener('input', function(e) {
|
||||
let value = e.target.value.replace(/\D/g, '');
|
||||
if (value.length >= 6) {
|
||||
value = value.substring(0,3) + '-' + value.substring(3,5) + '-' + value.substring(5,9);
|
||||
} else if (value.length >= 4) {
|
||||
value = value.substring(0,3) + '-' + value.substring(3);
|
||||
}
|
||||
e.target.value = value;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block js %}{% endblock %}
|
||||
@ -382,7 +382,7 @@
|
||||
<div class="text-center text-muted py-3">
|
||||
<i class="fas fa-shield-alt fa-2x mb-2"></i>
|
||||
<p class="small">No insurance information</p>
|
||||
<a href="{% url 'patients:insurance_form' %}?patient={{ object.pk }}" class="btn btn-sm btn-primary">
|
||||
<a href="{% url 'patients:insurance_create' object.pk %}" class="btn btn-sm btn-primary">
|
||||
Add Insurance
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@ -19,6 +19,7 @@
|
||||
</a>
|
||||
<button type="button" class="btn btn-outline-secondary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown">
|
||||
<span class="visually-hidden">Toggle Dropdown</span>
|
||||
<i class="fas fa-ellipsis-v"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class="dropdown-item" href="#" onclick="exportPatients()">
|
||||
@ -232,7 +233,7 @@
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge bg-primary">{{ patient.medical_record_number }}</span>
|
||||
<span class="badge bg-primary">{{ patient.mrn }}</span>
|
||||
{% if patient.ssn %}
|
||||
<div class="small text-muted">SSN: ***-**-{{ patient.ssn|slice:"-4:" }}</div>
|
||||
{% endif %}
|
||||
@ -284,7 +285,7 @@
|
||||
title="View Profile">
|
||||
<i class="fas fa-eye"></i>
|
||||
</a>
|
||||
<a href="{% url 'patients:patient_form' patient.pk %}"
|
||||
<a href="{% url 'patients:patient_update' patient.pk %}"
|
||||
class="btn btn-outline-secondary"
|
||||
title="Edit">
|
||||
<i class="fas fa-edit"></i>
|
||||
@ -295,7 +296,7 @@
|
||||
title="Quick Actions">
|
||||
<i class="fas fa-bolt"></i>
|
||||
</button>
|
||||
<a href="{% url 'patients:patient_confirm_delete' patient.pk %}"
|
||||
<a href="{% url 'patients:patient_delete' patient.pk %}"
|
||||
class="btn btn-outline-danger"
|
||||
title="Delete">
|
||||
<i class="fas fa-trash"></i>
|
||||
@ -310,38 +311,7 @@
|
||||
|
||||
<!-- Pagination -->
|
||||
{% if is_paginated %}
|
||||
<div class="card-footer">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
Showing {{ page_obj.start_index }} to {{ page_obj.end_index }} of {{ paginator.count }} patients
|
||||
</div>
|
||||
<nav>
|
||||
<ul class="pagination pagination-sm mb-0">
|
||||
{% if page_obj.has_previous %}
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="?page=1{% if request.GET.search %}&search={{ request.GET.search }}{% endif %}{% if request.GET.gender %}&gender={{ request.GET.gender }}{% endif %}">First</a>
|
||||
</li>
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="?page={{ page_obj.previous_page_number }}{% if request.GET.search %}&search={{ request.GET.search }}{% endif %}{% if request.GET.gender %}&gender={{ request.GET.gender }}{% endif %}">Previous</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
<li class="page-item active">
|
||||
<span class="page-link">{{ page_obj.number }}</span>
|
||||
</li>
|
||||
|
||||
{% if page_obj.has_next %}
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="?page={{ page_obj.next_page_number }}{% if request.GET.search %}&search={{ request.GET.search }}{% endif %}{% if request.GET.gender %}&gender={{ request.GET.gender }}{% endif %}">Next</a>
|
||||
</li>
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="?page={{ paginator.num_pages }}{% if request.GET.search %}&search={{ request.GET.search }}{% endif %}{% if request.GET.gender %}&gender={{ request.GET.gender }}{% endif %}">Last</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
{% include 'partial/pagination.html' %}
|
||||
{% endif %}
|
||||
|
||||
<!-- Bulk Actions -->
|
||||
@ -477,31 +447,31 @@ function quickActions(patientId) {
|
||||
|
||||
function scheduleAppointment() {
|
||||
if (currentPatientId) {
|
||||
window.location.href = `{% url 'appointments:appointment_form' %}?patient=${currentPatientId}`;
|
||||
window.location.href = `{% url 'appointments:appointment_create' %}?patient=${currentPatientId}`;
|
||||
}
|
||||
}
|
||||
|
||||
function newEncounter() {
|
||||
if (currentPatientId) {
|
||||
window.location.href = `{% url 'emr:encounter_form' %}?patient=${currentPatientId}`;
|
||||
window.location.href = `{% url 'emr:encounter_create' %}?patient=${currentPatientId}`;
|
||||
}
|
||||
}
|
||||
|
||||
function orderLabTest() {
|
||||
if (currentPatientId) {
|
||||
window.location.href = `{% url 'laboratory:lab_order_form' %}?patient=${currentPatientId}`;
|
||||
window.location.href = `{% url 'laboratory:lab_order_create' %}?patient=${currentPatientId}`;
|
||||
}
|
||||
}
|
||||
|
||||
function newPrescription() {
|
||||
if (currentPatientId) {
|
||||
window.location.href = `{% url 'pharmacy:prescription_form' %}?patient=${currentPatientId}`;
|
||||
window.location.href = `{% url 'pharmacy:prescription_create' %}?patient=${currentPatientId}`;
|
||||
}
|
||||
}
|
||||
|
||||
function addNote() {
|
||||
if (currentPatientId) {
|
||||
window.location.href = `{% url 'patients:patient_note_form' %}?patient=${currentPatientId}`;
|
||||
window.location.href = `{% url 'patients:patient_note_create' 0 %}`.replace('0', currentPatientId)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user