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>
|
</content>
|
||||||
<orderEntry type="jdk" jdkName="uv (hospital_management_system_v4)" jdkType="Python SDK" />
|
<orderEntry type="jdk" jdkName="uv (hospital_management_system_v4)" jdkType="Python SDK" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
<orderEntry type="library" name="toastr.js" level="application" />
|
||||||
</component>
|
</component>
|
||||||
<component name="PyDocumentationSettings">
|
<component name="PyDocumentationSettings">
|
||||||
<option name="format" value="PLAIN" />
|
<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('reschedule/<int:appointment_id>/', views.reschedule_appointment, name='reschedule_appointment'),
|
||||||
path('cancel/<int:appointment_id>/', views.cancel_appointment, name='cancel_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
|
# API endpoints
|
||||||
# path('api/', include('appointments.api.urls')),
|
# path('api/', include('appointments.api.urls')),
|
||||||
]
|
]
|
||||||
|
|||||||
@ -1002,7 +1002,7 @@ class AppointmentTemplateListView(LoginRequiredMixin, ListView):
|
|||||||
List appointment templates.
|
List appointment templates.
|
||||||
"""
|
"""
|
||||||
model = AppointmentTemplate
|
model = AppointmentTemplate
|
||||||
template_name = 'appointments/appointment_template_list.html'
|
template_name = 'appointments/templates/appointment_template_list.html'
|
||||||
context_object_name = 'templates'
|
context_object_name = 'templates'
|
||||||
paginate_by = 25
|
paginate_by = 25
|
||||||
|
|
||||||
@ -1066,7 +1066,7 @@ class AppointmentTemplateDetailView(LoginRequiredMixin, DetailView):
|
|||||||
Display appointment template details.
|
Display appointment template details.
|
||||||
"""
|
"""
|
||||||
model = AppointmentTemplate
|
model = AppointmentTemplate
|
||||||
template_name = 'appointments/appointment_template_detail.html'
|
template_name = 'appointments/templates/appointment_template_detail.html'
|
||||||
context_object_name = 'template'
|
context_object_name = 'template'
|
||||||
|
|
||||||
def get_queryset(self):
|
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 = [
|
list_display = [
|
||||||
'mrn', 'get_full_name', 'date_of_birth', 'age', 'gender',
|
'mrn', 'get_full_name', 'date_of_birth', 'age', 'gender',
|
||||||
'phone_number', 'is_active',
|
'mobile_number', 'is_active',
|
||||||
]
|
]
|
||||||
list_filter = [
|
list_filter = [
|
||||||
'tenant', 'gender', 'race', 'ethnicity', 'marital_status',
|
'tenant', 'gender', 'marital_status',
|
||||||
'is_active', 'is_deceased', 'is_vip', 'confidential_patient',
|
'is_active', 'is_deceased', 'is_vip', 'confidential_patient',
|
||||||
|
|
||||||
]
|
]
|
||||||
search_fields = [
|
search_fields = [
|
||||||
'mrn', 'first_name', 'last_name', 'email', 'phone_number',
|
'mrn', 'id_number', 'first_name', 'last_name', 'email', 'mobile_number',
|
||||||
'ssn', 'drivers_license'
|
|
||||||
]
|
]
|
||||||
ordering = ['last_name', 'first_name']
|
ordering = ['last_name', 'first_name']
|
||||||
|
|
||||||
@ -80,8 +79,7 @@ class PatientProfileAdmin(admin.ModelAdmin):
|
|||||||
}),
|
}),
|
||||||
('Demographics', {
|
('Demographics', {
|
||||||
'fields': (
|
'fields': (
|
||||||
'date_of_birth', 'gender', 'sex_assigned_at_birth',
|
'date_of_birth', 'gender', 'marital_status'
|
||||||
'race', 'ethnicity', 'marital_status'
|
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
('Contact Information', {
|
('Contact Information', {
|
||||||
@ -91,9 +89,6 @@ class PatientProfileAdmin(admin.ModelAdmin):
|
|||||||
'zip_code', 'country'
|
'zip_code', 'country'
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
('Identification', {
|
|
||||||
'fields': ('ssn', 'drivers_license', 'drivers_license_state')
|
|
||||||
}),
|
|
||||||
('Language and Communication', {
|
('Language and Communication', {
|
||||||
'fields': (
|
'fields': (
|
||||||
'primary_language', 'interpreter_needed', 'communication_preference'
|
'primary_language', 'interpreter_needed', 'communication_preference'
|
||||||
|
|||||||
@ -19,10 +19,9 @@ class PatientProfileForm(forms.ModelForm):
|
|||||||
model = PatientProfile
|
model = PatientProfile
|
||||||
fields = [
|
fields = [
|
||||||
'first_name', 'last_name', 'middle_name', 'preferred_name', 'suffix',
|
'first_name', 'last_name', 'middle_name', 'preferred_name', 'suffix',
|
||||||
'date_of_birth', 'gender', 'sex_assigned_at_birth', 'race', 'ethnicity',
|
'id_number', 'date_of_birth', 'gender', 'phone_number', 'mobile_number',
|
||||||
'phone_number', 'mobile_number', 'email', 'address_line_1', 'address_line_2',
|
'email', 'address_line_1', 'address_line_2', 'city', 'state', 'zip_code',
|
||||||
'city', 'state', 'zip_code', 'country', 'marital_status', 'occupation',
|
'country', 'marital_status', 'occupation', 'is_active'
|
||||||
'is_active'
|
|
||||||
]
|
]
|
||||||
widgets = {
|
widgets = {
|
||||||
'first_name': forms.TextInput(attrs={'class': 'form-control'}),
|
'first_name': forms.TextInput(attrs={'class': 'form-control'}),
|
||||||
@ -30,11 +29,9 @@ class PatientProfileForm(forms.ModelForm):
|
|||||||
'middle_name': forms.TextInput(attrs={'class': 'form-control'}),
|
'middle_name': forms.TextInput(attrs={'class': 'form-control'}),
|
||||||
'preferred_name': forms.TextInput(attrs={'class': 'form-control'}),
|
'preferred_name': forms.TextInput(attrs={'class': 'form-control'}),
|
||||||
'suffix': 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'}),
|
'date_of_birth': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}),
|
||||||
'gender': forms.Select(attrs={'class': 'form-select'}),
|
'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'}),
|
'phone_number': forms.TextInput(attrs={'class': 'form-control'}),
|
||||||
'mobile_number': forms.TextInput(attrs={'class': 'form-control'}),
|
'mobile_number': forms.TextInput(attrs={'class': 'form-control'}),
|
||||||
'email': forms.EmailInput(attrs={'class': 'form-control'}),
|
'email': forms.EmailInput(attrs={'class': 'form-control'}),
|
||||||
@ -120,7 +117,7 @@ class InsuranceInfoForm(forms.ModelForm):
|
|||||||
fields = [
|
fields = [
|
||||||
'insurance_type', 'insurance_company', 'plan_name', 'plan_type',
|
'insurance_type', 'insurance_company', 'plan_name', 'plan_type',
|
||||||
'policy_number', 'group_number', 'subscriber_name', 'subscriber_relationship',
|
'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',
|
'copay_amount', 'deductible_amount', 'out_of_pocket_max', 'is_verified',
|
||||||
'requires_authorization'
|
'requires_authorization'
|
||||||
]
|
]
|
||||||
@ -134,7 +131,7 @@ class InsuranceInfoForm(forms.ModelForm):
|
|||||||
'subscriber_name': forms.TextInput(attrs={'class': 'form-control'}),
|
'subscriber_name': forms.TextInput(attrs={'class': 'form-control'}),
|
||||||
'subscriber_relationship': forms.Select(attrs={'class': 'form-select'}),
|
'subscriber_relationship': forms.Select(attrs={'class': 'form-select'}),
|
||||||
'subscriber_dob': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}),
|
'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'}),
|
'effective_date': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}),
|
||||||
'termination_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'}),
|
'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'),
|
('UNKNOWN', 'Unknown'),
|
||||||
('PREFER_NOT_TO_SAY', 'Prefer not to say'),
|
('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 = [
|
MARITAL_STATUS_CHOICES = [
|
||||||
('SINGLE', 'Single'),
|
('SINGLE', 'Single'),
|
||||||
('MARRIED', 'Married'),
|
('MARRIED', 'Married'),
|
||||||
@ -128,29 +106,6 @@ class PatientProfile(models.Model):
|
|||||||
choices=GENDER_CHOICES,
|
choices=GENDER_CHOICES,
|
||||||
help_text='Gender'
|
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
|
# Contact Information
|
||||||
email = models.EmailField(
|
email = models.EmailField(
|
||||||
@ -212,32 +167,20 @@ class PatientProfile(models.Model):
|
|||||||
)
|
)
|
||||||
country = models.CharField(
|
country = models.CharField(
|
||||||
max_length=100,
|
max_length=100,
|
||||||
default='United States',
|
default='Saudi Arabia',
|
||||||
help_text='Country'
|
help_text='Country'
|
||||||
)
|
)
|
||||||
|
|
||||||
# Social Security and Identification
|
id_number = models.CharField(
|
||||||
ssn = models.CharField(
|
max_length=10,
|
||||||
max_length=11,
|
unique=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
validators=[RegexValidator(
|
validators=[RegexValidator(
|
||||||
regex=r'^\d{3}-\d{2}-\d{4}$',
|
regex=r'^\d{10}$',
|
||||||
message='SSN must be in format: 123-45-6789'
|
message='Saudi National ID must be exactly 10 digits'
|
||||||
)],
|
)],
|
||||||
help_text='Social Security Number'
|
help_text='Saudi National ID (10 digits)'
|
||||||
)
|
|
||||||
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'
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Marital Status and Family
|
# Marital Status and Family
|
||||||
@ -252,7 +195,7 @@ class PatientProfile(models.Model):
|
|||||||
# Language and Communication
|
# Language and Communication
|
||||||
primary_language = models.CharField(
|
primary_language = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
default='English',
|
default='Arabic',
|
||||||
help_text='Primary language'
|
help_text='Primary language'
|
||||||
)
|
)
|
||||||
interpreter_needed = models.BooleanField(
|
interpreter_needed = models.BooleanField(
|
||||||
@ -358,7 +301,6 @@ class PatientProfile(models.Model):
|
|||||||
help_text='User who registered the patient'
|
help_text='User who registered the patient'
|
||||||
)
|
)
|
||||||
|
|
||||||
# Photo
|
|
||||||
photo = models.ImageField(
|
photo = models.ImageField(
|
||||||
upload_to='patient_photos/',
|
upload_to='patient_photos/',
|
||||||
blank=True,
|
blank=True,
|
||||||
@ -366,7 +308,6 @@ class PatientProfile(models.Model):
|
|||||||
help_text='Patient photo'
|
help_text='Patient photo'
|
||||||
)
|
)
|
||||||
|
|
||||||
# Metadata
|
|
||||||
created_at = models.DateTimeField(auto_now_add=True)
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
updated_at = models.DateTimeField(auto_now=True)
|
updated_at = models.DateTimeField(auto_now=True)
|
||||||
last_visit_date = models.DateTimeField(
|
last_visit_date = models.DateTimeField(
|
||||||
@ -384,8 +325,8 @@ class PatientProfile(models.Model):
|
|||||||
models.Index(fields=['tenant', 'mrn']),
|
models.Index(fields=['tenant', 'mrn']),
|
||||||
models.Index(fields=['last_name', 'first_name']),
|
models.Index(fields=['last_name', 'first_name']),
|
||||||
models.Index(fields=['date_of_birth']),
|
models.Index(fields=['date_of_birth']),
|
||||||
models.Index(fields=['ssn']),
|
models.Index(fields=['id_number']),
|
||||||
models.Index(fields=['phone_number']),
|
models.Index(fields=['mobile_number']),
|
||||||
models.Index(fields=['email']),
|
models.Index(fields=['email']),
|
||||||
]
|
]
|
||||||
unique_together = ['tenant', 'mrn']
|
unique_together = ['tenant', 'mrn']
|
||||||
@ -711,15 +652,16 @@ class InsuranceInfo(models.Model):
|
|||||||
null=True,
|
null=True,
|
||||||
help_text='Subscriber date of birth'
|
help_text='Subscriber date of birth'
|
||||||
)
|
)
|
||||||
subscriber_ssn = models.CharField(
|
subscriber_id_number = models.CharField(
|
||||||
max_length=11,
|
max_length=10,
|
||||||
|
unique=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
validators=[RegexValidator(
|
validators=[RegexValidator(
|
||||||
regex=r'^\d{3}-\d{2}-\d{4}$',
|
regex=r'^\d{10}$',
|
||||||
message='SSN must be in format: 123-45-6789'
|
message='Saudi National ID must be exactly 10 digits'
|
||||||
)],
|
)],
|
||||||
help_text='Subscriber Social Security Number'
|
help_text='Saudi National ID (10 digits)'
|
||||||
)
|
)
|
||||||
|
|
||||||
# Coverage Information
|
# Coverage Information
|
||||||
|
|||||||
@ -38,7 +38,7 @@ urlpatterns = [
|
|||||||
path('patient-search/', views.patient_search, name='patient_search'),
|
path('patient-search/', views.patient_search, name='patient_search'),
|
||||||
path('patient-stats/', views.patient_stats, name='patient_stats'),
|
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('<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/<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/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'),
|
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.
|
Patient listing view.
|
||||||
"""
|
"""
|
||||||
model = PatientProfile
|
model = PatientProfile
|
||||||
template_name = 'patients/patient_list.html'
|
template_name = 'patients/profiles/patient_list.html'
|
||||||
context_object_name = 'patients'
|
context_object_name = 'patients'
|
||||||
paginate_by = 25
|
paginate_by = 25
|
||||||
|
|
||||||
@ -610,7 +610,7 @@ class InsuranceInfoCreateView(LoginRequiredMixin, PermissionRequiredMixin, Creat
|
|||||||
form_class = InsuranceInfoForm
|
form_class = InsuranceInfoForm
|
||||||
template_name = "patients/insurance/insurance_form.html"
|
template_name = "patients/insurance/insurance_form.html"
|
||||||
permission_required = "patients.add_insuranceinfo"
|
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):
|
def dispatch(self, request, *args, **kwargs):
|
||||||
self.tenant = getattr(request, "tenant", None)
|
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%}
|
{% load i18n static%}
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
{% get_current_language as LANGUAGE_CODE %}
|
{% get_current_language as LANGUAGE_CODE %}
|
||||||
@ -19,22 +17,9 @@
|
|||||||
|
|
||||||
<link href="{% static 'css/vendor.min.css' %}" rel="stylesheet" />
|
<link href="{% static 'css/vendor.min.css' %}" rel="stylesheet" />
|
||||||
<link href="{% static 'css/default/app.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/custom.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 -->
|
|
||||||
|
|
||||||
<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 ================== -->
|
<!-- ================== END core-css ================== -->
|
||||||
|
|
||||||
{% block css %}
|
{% block css %}
|
||||||
@ -168,7 +153,7 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
|
||||||
<div hx-boost="false">
|
<div>
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@ -184,72 +169,18 @@
|
|||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
<!-- ================== BEGIN core-js ================== -->
|
<!-- ================== BEGIN core-js ================== -->
|
||||||
|
<script src="{% static 'js/htmx.min.js' %}"></script>
|
||||||
<script src="{% static 'js/vendor.min.js' %}"></script>
|
<script src="{% static 'js/vendor.min.js' %}"></script>
|
||||||
<script src="{% static 'js/app.min.js' %}"></script>
|
<script src="{% static 'js/app.min.js' %}"></script>
|
||||||
|
{#<script src="{% static 'js/custom.js' %}"></script>#}
|
||||||
|
|
||||||
|
|
||||||
<!-- ================== END core-js ================== -->
|
<!-- ================== END core-js ================== -->
|
||||||
|
|
||||||
{% block 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 %}
|
{% endblock %}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@ -3,9 +3,9 @@
|
|||||||
|
|
||||||
{% block title %}Patients Dashboard{% endblock %}
|
{% block title %}Patients Dashboard{% endblock %}
|
||||||
|
|
||||||
{% block extra_css %}
|
{% block css %}
|
||||||
<link href="{% static 'assets/plugins/datatables.net-bs5/css/dataTables.bootstrap5.min.css' %}" rel="stylesheet" />
|
<link href="{% static '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" />
|
<link href="{% static 'plugins/datatables.net-responsive-bs5/css/responsive.bootstrap5.min.css' %}" rel="stylesheet" />
|
||||||
<style>
|
<style>
|
||||||
.dashboard-header {
|
.dashboard-header {
|
||||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
@ -830,10 +830,10 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block extra_js %}
|
{% block js %}
|
||||||
<script src="{% static 'assets/plugins/chart.js/chart.min.js' %}"></script>
|
<script src="{% static 'plugins/chart.js/dist/chart.js' %}"></script>
|
||||||
<script src="{% static 'assets/plugins/datatables.net/js/jquery.dataTables.min.js' %}"></script>
|
<script src="{% static 'plugins/datatables.net/js/dataTables.min.js' %}"></script>
|
||||||
<script src="{% static 'assets/plugins/datatables.net-bs5/js/dataTables.bootstrap5.min.js' %}"></script>
|
<script src="{% static 'plugins/datatables.net-bs5/js/dataTables.bootstrap5.min.js' %}"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
{% block title %}{% if object %}Edit{% else %}Add{% endif %} Insurance - Patients{% endblock %}
|
{% block title %}{% if object %}Edit{% else %}Add{% endif %} Insurance - Patients{% endblock %}
|
||||||
|
|
||||||
{% block css %}
|
{% 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 %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
@ -12,12 +12,18 @@
|
|||||||
<ol class="breadcrumb float-xl-end">
|
<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 'core:dashboard' %}">Dashboard</a></li>
|
||||||
<li class="breadcrumb-item"><a href="{% url 'patients:patient_list' %}">Patients</a></li>
|
<li class="breadcrumb-item"><a href="{% url 'patients:patient_list' %}">Patients</a></li>
|
||||||
|
|
||||||
{% if object %}
|
{% 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">
|
||||||
<li class="breadcrumb-item"><a href="{% url 'patients:insurance_detail' object.pk %}">{{ object.insurance_provider }}</a></li>
|
<a href="{% url 'patients:patient_detail' object.patient.pk %}">{{ object.patient.get_full_name }}</a>
|
||||||
<li class="breadcrumb-item active">Edit</li>
|
</li>
|
||||||
|
<li class="breadcrumb-item active">Edit Insurance</li>
|
||||||
{% else %}
|
{% else %}
|
||||||
<li class="breadcrumb-item"><a href="{% url 'patients:insurance_list' %}">Insurance</a></li>
|
{% 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>
|
<li class="breadcrumb-item active">Add Insurance</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</ol>
|
</ol>
|
||||||
@ -26,7 +32,9 @@
|
|||||||
<!-- BEGIN page-header -->
|
<!-- BEGIN page-header -->
|
||||||
<h1 class="page-header">
|
<h1 class="page-header">
|
||||||
{% if object %}Edit Insurance{% else %}Add Insurance{% endif %}
|
{% if object %}Edit Insurance{% else %}Add Insurance{% endif %}
|
||||||
<small>{% if object %}{{ object.insurance_provider }}{% else %}New Insurance Policy{% endif %}</small>
|
<small>
|
||||||
|
{% if object %}{{ object.insurance_company }}{% elif patient %}for {{ patient.get_full_name }}{% else %}New Insurance Policy{% endif %}
|
||||||
|
</small>
|
||||||
</h1>
|
</h1>
|
||||||
<!-- END page-header -->
|
<!-- END page-header -->
|
||||||
|
|
||||||
@ -37,178 +45,203 @@
|
|||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
<h4 class="panel-title">Insurance Information</h4>
|
<h4 class="panel-title">Insurance Information</h4>
|
||||||
<div class="panel-heading-btn">
|
<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>
|
<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>
|
</div>
|
||||||
|
|
||||||
<div class="panel-body">
|
<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 method="post" id="insurance-form" novalidate>
|
<form method="post" id="insurance-form" novalidate>
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
|
||||||
|
<!-- POLICY BASICS -->
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div class="form-floating">
|
<div class="mb-3">
|
||||||
{{ form.patient }}
|
<label for="{{ form.insurance_type.id_for_label }}" class="form-label">Insurance Type <span class="text-danger">*</span></label>
|
||||||
<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 }}
|
{{ 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 %}
|
||||||
{% if form.insurance_type.errors %}
|
</div>
|
||||||
<div class="invalid-feedback d-block">{{ form.insurance_type.errors.0 }}</div>
|
</div>
|
||||||
{% endif %}
|
|
||||||
|
<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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div class="form-floating">
|
<div class="mb-3">
|
||||||
{{ form.insurance_provider }}
|
<label for="{{ form.plan_name.id_for_label }}" class="form-label">Plan Name</label>
|
||||||
<label for="{{ form.insurance_provider.id_for_label }}">Insurance Provider <span class="text-danger">*</span></label>
|
{{ form.plan_name }}
|
||||||
{% if form.insurance_provider.errors %}
|
{% if form.plan_name.errors %}<div class="invalid-feedback d-block">{{ form.plan_name.errors.0 }}</div>{% endif %}
|
||||||
<div class="invalid-feedback d-block">{{ form.insurance_provider.errors.0 }}</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div class="form-floating">
|
<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 }}
|
{{ 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 %}
|
||||||
{% if form.policy_number.errors %}
|
|
||||||
<div class="invalid-feedback d-block">{{ form.policy_number.errors.0 }}</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row mb-3">
|
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div class="form-floating">
|
<div class="mb-3">
|
||||||
{{ form.member_id }}
|
<label for="{{ form.group_number.id_for_label }}" class="form-label">Group Number</label>
|
||||||
<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 }}
|
{{ 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 %}
|
||||||
{% if form.group_number.errors %}
|
|
||||||
<div class="invalid-feedback d-block">{{ form.group_number.errors.0 }}</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</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="row mb-3">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div class="form-floating">
|
<div class="mb-3">
|
||||||
|
<label for="{{ form.effective_date.id_for_label }}" class="form-label">Effective Date</label>
|
||||||
{{ form.effective_date }}
|
{{ 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 %}
|
||||||
{% 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>
|
</div>
|
||||||
|
|
||||||
<div class="row mb-3">
|
|
||||||
<div class="col-md-6">
|
<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">
|
<div class="mb-3">
|
||||||
<label for="{{ form.coverage_details.id_for_label }}" class="form-label">Coverage Details</label>
|
<label for="{{ form.termination_date.id_for_label }}" class="form-label">Termination Date</label>
|
||||||
{{ form.coverage_details }}
|
{{ form.termination_date }}
|
||||||
{% if form.coverage_details.errors %}
|
{% if form.termination_date.errors %}<div class="invalid-feedback d-block">{{ form.termination_date.errors.0 }}</div>{% endif %}
|
||||||
<div class="invalid-feedback d-block">{{ form.coverage_details.errors.0 }}</div>
|
</div>
|
||||||
{% endif %}
|
</div>
|
||||||
<div class="form-text">Describe what services are covered, limitations, and special requirements.</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- FINANCIALS -->
|
||||||
|
<div class="row mb-3">
|
||||||
|
<div class="col-md-4">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="{{ form.notes.id_for_label }}" class="form-label">Additional Notes</label>
|
<label for="{{ form.copay_amount.id_for_label }}" class="form-label">Copay Amount ($)</label>
|
||||||
{{ form.notes }}
|
{{ form.copay_amount }}
|
||||||
{% if form.notes.errors %}
|
{% if form.copay_amount.errors %}<div class="invalid-feedback d-block">{{ form.copay_amount.errors.0 }}</div>{% endif %}
|
||||||
<div class="invalid-feedback d-block">{{ form.notes.errors.0 }}</div>
|
</div>
|
||||||
{% endif %}
|
|
||||||
<div class="form-text">Any additional information about this insurance policy.</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Form Actions -->
|
<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 class="d-flex justify-content-between">
|
||||||
<div>
|
<div>
|
||||||
<a href="{% if object %}{% url 'patients:insurance_detail' object.pk %}{% else %}{% url 'patients:insurance_list' %}{% endif %}" class="btn btn-secondary">
|
<a href="{% url 'patients:insurance_info_list' patient.pk %}" class="btn btn-secondary">
|
||||||
<i class="fa fa-arrow-left me-2"></i>Cancel
|
<i class="fa fa-arrow-left me-2"></i>Cancel
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<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">
|
<button type="submit" class="btn btn-primary">
|
||||||
<i class="fa fa-check me-2"></i>{% if object %}Update{% else %}Create{% endif %} Insurance
|
<i class="fa fa-check me-2"></i>{% if object %}Update{% else %}Create{% endif %} Insurance
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -216,138 +249,87 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-xl-4">
|
<div class="col-xl-4">
|
||||||
<!-- BEGIN panel -->
|
<!-- 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 panel-inverse">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
<h4 class="panel-title">Help & Guidelines</h4>
|
<h4 class="panel-title">Help & Guidelines</h4>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<div class="alert alert-info">
|
<div class="alert alert-info">
|
||||||
<h6 class="alert-heading">Insurance Information Tips</h6>
|
<h6 class="alert-heading">Tips</h6>
|
||||||
<ul class="mb-0 small">
|
<ul class="mb-0 small">
|
||||||
<li>Verify all information with the insurance card</li>
|
<li>Verify against the insurance card.</li>
|
||||||
<li>Primary insurance should be billed first</li>
|
<li>Check effective and termination dates.</li>
|
||||||
<li>Check effective and expiration dates carefully</li>
|
<li>Record subscriber info if different from patient.</li>
|
||||||
<li>Include group number if available</li>
|
<li>Mark “Requires Authorization” when pre-auth is needed.</li>
|
||||||
<li>Document any special coverage requirements</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card border-primary mb-3">
|
<div class="card border-primary mb-3">
|
||||||
<div class="card-header bg-primary text-white">
|
<div class="card-header bg-primary text-white">
|
||||||
<h6 class="card-title mb-0">Required Fields</h6>
|
<h6 class="card-title mb-0">Frequently Required</h6>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body small">
|
||||||
<ul class="list-unstyled mb-0 small">
|
<ul class="list-unstyled mb-0">
|
||||||
<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>Insurance Type</li>
|
||||||
<li><i class="fa fa-check text-success me-2"></i>Status</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>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card border-warning mb-3">
|
<div class="card border-warning">
|
||||||
<div class="card-header bg-warning text-dark">
|
<div class="card-header bg-warning text-dark">
|
||||||
<h6 class="card-title mb-0">Validation Rules</h6>
|
<h6 class="card-title mb-0">Validation Rules</h6>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body small">
|
||||||
<ul class="list-unstyled mb-0 small">
|
<ul class="list-unstyled mb-0">
|
||||||
<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>Termination date must be after effective date.</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>Policy number often must be unique by company.</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>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</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 -->
|
|
||||||
{% 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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<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>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- END panel -->
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Verification Modal -->
|
<!-- Verification Modal (hooks kept for later integrations) -->
|
||||||
<div class="modal fade" id="verificationModal" tabindex="-1">
|
<div class="modal fade" id="verificationModal" tabindex="-1">
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
@ -356,9 +338,7 @@
|
|||||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div id="verification-results">
|
<div id="verification-results"></div>
|
||||||
<!-- Verification results will be loaded here -->
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||||
@ -369,231 +349,63 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block js %}
|
{% 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>
|
<script>
|
||||||
$(document).ready(function() {
|
$(function () {
|
||||||
// Initialize date pickers
|
// Datepickers (optional; widgets already type="date")
|
||||||
$('#{{ form.effective_date.id_for_label }}, #{{ form.expiration_date.id_for_label }}').datepicker({
|
$('#{{ form.effective_date.id_for_label }}, #{{ form.termination_date.id_for_label }}').datepicker({
|
||||||
format: 'yyyy-mm-dd',
|
format: 'yyyy-mm-dd',
|
||||||
autoclose: true,
|
autoclose: true,
|
||||||
todayHighlight: true
|
todayHighlight: true
|
||||||
});
|
});
|
||||||
|
|
||||||
// Form validation
|
// Client-side sanity check for dates
|
||||||
$('#insurance-form').on('submit', function(e) {
|
function validateDates() {
|
||||||
if (!validateForm()) {
|
const eff = $('#{{ form.effective_date.id_for_label }}').val();
|
||||||
e.preventDefault();
|
const term = $('#{{ form.termination_date.id_for_label }}').val();
|
||||||
}
|
if (eff && term) {
|
||||||
});
|
const e = new Date(eff);
|
||||||
|
const t = new Date(term);
|
||||||
// Auto-save functionality
|
if (t <= e) {
|
||||||
var autoSaveTimer;
|
$('#{{ form.termination_date.id_for_label }}').addClass('is-invalid');
|
||||||
$('#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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
$('#{{ form.expiration_date.id_for_label }}').removeClass('is-invalid');
|
$('#{{ form.termination_date.id_for_label }}').removeClass('is-invalid');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
|
|
||||||
function checkPrimaryInsurance() {
|
|
||||||
var patientId = $('#{{ form.patient.id_for_label }}').val();
|
|
||||||
if (!patientId) return;
|
|
||||||
|
|
||||||
$.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.');
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function validatePolicyNumber() {
|
$('#{{ form.effective_date.id_for_label }}, #{{ form.termination_date.id_for_label }}').on('change', validateDates);
|
||||||
var policyNumber = $('#{{ form.policy_number.id_for_label }}').val();
|
|
||||||
var provider = $('#{{ form.insurance_provider.id_for_label }}').val();
|
|
||||||
|
|
||||||
if (policyNumber && provider) {
|
// Minimal required validation on submit (server remains source of truth)
|
||||||
$.ajax({
|
$('#insurance-form').on('submit', function (e) {
|
||||||
url: '{% url "patients:validate_policy_number" %}',
|
let ok = true;
|
||||||
method: 'POST',
|
const requiredIds = [
|
||||||
data: {
|
'{{ form.insurance_type.id_for_label }}',
|
||||||
'csrfmiddlewaretoken': '{{ csrf_token }}',
|
'{{ form.insurance_company.id_for_label }}',
|
||||||
'policy_number': policyNumber,
|
'{{ form.policy_number.id_for_label }}'
|
||||||
'provider': provider,
|
];
|
||||||
'current_insurance_id': {% if object %}'{{ object.pk }}'{% else %}null{% endif %}
|
requiredIds.forEach(function(id){
|
||||||
},
|
const $f = $('#'+id);
|
||||||
success: function(response) {
|
if(!$f.val()){
|
||||||
if (!response.is_valid) {
|
$f.addClass('is-invalid');
|
||||||
$('#{{ form.policy_number.id_for_label }}').addClass('is-invalid');
|
ok = false;
|
||||||
toastr.error('Policy number already exists for this provider');
|
|
||||||
} else {
|
} else {
|
||||||
$('#{{ form.policy_number.id_for_label }}').removeClass('is-invalid');
|
$f.removeClass('is-invalid');
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
if (!validateDates()) ok = false;
|
||||||
}
|
if (!ok) {
|
||||||
|
e.preventDefault();
|
||||||
function saveDraft() {
|
alert('Please fill required fields and fix highlighted errors.');
|
||||||
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) {
|
// Optional: simple uniqueness hint (adjust endpoint if you add it)
|
||||||
var alertClass = 'alert-' + type;
|
$('#{{ form.policy_number.id_for_label }}, #{{ form.insurance_company.id_for_label }}').on('blur', function(){
|
||||||
$('#form-status').html('<div class="alert ' + alertClass + '"><i class="fa fa-info-circle me-2"></i>' + message + '</div>');
|
// Placeholder for future AJAX uniqueness check
|
||||||
}
|
|
||||||
|
|
||||||
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!');
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|||||||
@ -15,8 +15,7 @@
|
|||||||
<span class="badge bg-success">Verified</span>
|
<span class="badge bg-success">Verified</span>
|
||||||
{% else %}
|
{% else %}
|
||||||
<button class="btn btn-sm btn-outline-primary"
|
<button class="btn btn-sm btn-outline-primary"
|
||||||
hx-post="{% url 'patients:verify_insurance' insurance.id %}"
|
hx-post="{% url 'patients:verify_insurance' insurance.id %}">
|
||||||
hx-swap="outerHTML">
|
|
||||||
Verify
|
Verify
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@ -45,9 +45,6 @@
|
|||||||
</div>
|
</div>
|
||||||
<div>{{ patient.get_gender_display }}</div>
|
<div>{{ patient.get_gender_display }}</div>
|
||||||
<div>DOB: {{ patient.date_of_birth|date:"M d, Y" }}</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>
|
||||||
<td>
|
<td>
|
||||||
{% if patient.email %}
|
{% if patient.email %}
|
||||||
@ -73,9 +70,9 @@
|
|||||||
<div>{{ primary_insurance.insurance_company }}</div>
|
<div>{{ primary_insurance.insurance_company }}</div>
|
||||||
<small class="text-muted">{{ primary_insurance.plan_name }}</small>
|
<small class="text-muted">{{ primary_insurance.plan_name }}</small>
|
||||||
{% if primary_insurance.is_verified %}
|
{% if primary_insurance.is_verified %}
|
||||||
<br><span class="badge bg-success">Verified</span>
|
<br><span class="badge bg-success fs-9px">Verified</span>
|
||||||
{% else %}
|
{% else %}
|
||||||
<br><span class="badge bg-warning">Unverified</span>
|
<br><span class="badge bg-warning fs-9px">Unverified</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% else %}
|
{% else %}
|
||||||
<span class="text-muted">No insurance</span>
|
<span class="text-muted">No insurance</span>
|
||||||
@ -85,23 +82,23 @@
|
|||||||
<td>
|
<td>
|
||||||
<div>
|
<div>
|
||||||
{% if patient.is_deceased %}
|
{% if patient.is_deceased %}
|
||||||
<span class="badge bg-dark">Deceased</span>
|
<span class="badge bg-dark fs-9px">Deceased</span>
|
||||||
{% elif patient.is_active %}
|
{% elif patient.is_active %}
|
||||||
<span class="badge bg-success">Active</span>
|
<span class="badge bg-success fs-9px">Active</span>
|
||||||
{% else %}
|
{% else %}
|
||||||
<span class="badge bg-secondary">Inactive</span>
|
<span class="badge bg-secondary fs-9px">Inactive</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% if patient.allergies %}
|
{% if patient.allergies %}
|
||||||
<div class="mt-1">
|
<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
|
<i class="fas fa-exclamation-triangle"></i> Allergies
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if patient.medical_alerts %}
|
{% if patient.medical_alerts %}
|
||||||
<div class="mt-1">
|
<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
|
<i class="fas fa-bell"></i> Alerts
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@ -129,8 +126,10 @@
|
|||||||
<button type="button"
|
<button type="button"
|
||||||
class="btn btn-outline-info"
|
class="btn btn-outline-info"
|
||||||
title="View Insurance"
|
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-target="#insurance-modal-body"
|
||||||
|
hx-swap="innerHTML"
|
||||||
|
hx-trigger="click"
|
||||||
data-bs-toggle="modal"
|
data-bs-toggle="modal"
|
||||||
data-bs-target="#insuranceModal">
|
data-bs-target="#insuranceModal">
|
||||||
<i class="fas fa-shield-alt"></i>
|
<i class="fas fa-shield-alt"></i>
|
||||||
@ -138,12 +137,18 @@
|
|||||||
<button type="button"
|
<button type="button"
|
||||||
class="btn btn-outline-success"
|
class="btn btn-outline-success"
|
||||||
title="View Consents"
|
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-target="#consent-modal-body"
|
||||||
|
hx-swap="innerHTML"
|
||||||
|
hx-trigger="click"
|
||||||
data-bs-toggle="modal"
|
data-bs-toggle="modal"
|
||||||
data-bs-target="#consentModal">
|
data-bs-target="#consentModal">
|
||||||
<i class="fas fa-file-signature"></i>
|
<i class="fas fa-file-signature"></i>
|
||||||
</button>
|
</button>
|
||||||
|
<button type="button"
|
||||||
|
class="btn btn-outline-warning"
|
||||||
|
data-bs-toggle="modal"
|
||||||
|
data-bs-target="#quickActionsModal"></button>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@ -157,10 +162,11 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
|
||||||
|
<!-- prevents close & backdrop churn -->
|
||||||
|
|
||||||
<!-- Insurance Modal -->
|
<!-- 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-dialog modal-lg">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
@ -177,7 +183,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Consent Modal -->
|
<!-- 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-dialog modal-lg">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
@ -193,3 +199,54 @@
|
|||||||
</div>
|
</div>
|
||||||
</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>
|
<p>No patient notes</p>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|||||||
@ -7,15 +7,36 @@
|
|||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">
|
<div class="panel panel-inverse">
|
||||||
<h4 class="card-title mb-0">
|
<div class="panel-heading">
|
||||||
|
<h4 class="panel-title">
|
||||||
<i class="fas fa-user-plus me-2"></i>Patient Registration
|
<i class="fas fa-user-plus me-2"></i>Patient Registration
|
||||||
</h4>
|
</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>
|
||||||
|
|
||||||
<div class="card-body">
|
<div class="panel-body">
|
||||||
<form id="patient-registration-form" method="post">
|
{% 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 %}
|
{% csrf_token %}
|
||||||
|
|
||||||
<!-- Personal Information -->
|
<!-- Personal Information -->
|
||||||
@ -23,46 +44,76 @@
|
|||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<h5 class="border-bottom pb-2">Personal Information</h5>
|
<h5 class="border-bottom pb-2">Personal Information</h5>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="first_name" class="form-label">First Name *</label>
|
<label for="{{ form.first_name.id_for_label }}" class="form-label">First Name *</label>
|
||||||
<input type="text" class="form-control" id="first_name" name="first_name" required>
|
{{ form.first_name }}
|
||||||
|
{% if form.first_name.errors %}<div class="invalid-feedback d-block">{{ form.first_name.errors.0 }}</div>{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="last_name" class="form-label">Last Name *</label>
|
<label for="{{ form.last_name.id_for_label }}" class="form-label">Last Name *</label>
|
||||||
<input type="text" class="form-control" id="last_name" name="last_name" required>
|
{{ form.last_name }}
|
||||||
|
{% if form.last_name.errors %}<div class="invalid-feedback d-block">{{ form.last_name.errors.0 }}</div>{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="middle_name" class="form-label">Middle Name</label>
|
<label for="{{ form.middle_name.id_for_label }}" class="form-label">Middle Name</label>
|
||||||
<input type="text" class="form-control" id="middle_name" name="middle_name">
|
{{ form.middle_name }}
|
||||||
|
{% if form.middle_name.errors %}<div class="invalid-feedback d-block">{{ form.middle_name.errors.0 }}</div>{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="date_of_birth" class="form-label">Date of Birth *</label>
|
<label for="{{ form.preferred_name.id_for_label }}" class="form-label">Preferred Name</label>
|
||||||
<input type="date" class="form-control" id="date_of_birth" name="date_of_birth" required>
|
{{ form.preferred_name }}
|
||||||
|
{% if form.preferred_name.errors %}<div class="invalid-feedback d-block">{{ form.preferred_name.errors.0 }}</div>{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="gender" class="form-label">Gender *</label>
|
<label for="{{ form.suffix.id_for_label }}" class="form-label">Suffix</label>
|
||||||
<select class="form-select" id="gender" name="gender" required>
|
{{ form.suffix }}
|
||||||
<option value="">Select gender...</option>
|
{% if form.suffix.errors %}<div class="invalid-feedback d-block">{{ form.suffix.errors.0 }}</div>{% endif %}
|
||||||
<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>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="ssn" class="form-label">SSN</label>
|
<label for="{{ form.id_number.id_for_label }}" class="form-label">ID Number</label>
|
||||||
<input type="text" class="form-control" id="ssn" name="ssn" placeholder="XXX-XX-XXXX">
|
{{ 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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -72,159 +123,129 @@
|
|||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<h5 class="border-bottom pb-2">Contact Information</h5>
|
<h5 class="border-bottom pb-2">Contact Information</h5>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="phone_number" class="form-label">Phone Number</label>
|
<label for="{{ form.phone_number.id_for_label }}" class="form-label">Phone Number</label>
|
||||||
<input type="tel" class="form-control" id="phone_number" name="phone_number">
|
{{ form.phone_number }}
|
||||||
|
{% if form.phone_number.errors %}<div class="invalid-feedback d-block">{{ form.phone_number.errors.0 }}</div>{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="email" class="form-label">Email Address</label>
|
<label for="{{ form.mobile_number.id_for_label }}" class="form-label">Mobile Number</label>
|
||||||
<input type="email" class="form-control" id="email" name="email">
|
{{ form.mobile_number }}
|
||||||
|
{% if form.mobile_number.errors %}<div class="invalid-feedback d-block">{{ form.mobile_number.errors.0 }}</div>{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12">
|
|
||||||
|
<div class="col-md-6">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="address" class="form-label">Address</label>
|
<label for="{{ form.email.id_for_label }}" class="form-label">Email</label>
|
||||||
<textarea class="form-control" id="address" name="address" rows="2"></textarea>
|
{{ form.email }}
|
||||||
|
{% if form.email.errors %}<div class="invalid-feedback d-block">{{ form.email.errors.0 }}</div>{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Emergency Contact -->
|
<!-- Address -->
|
||||||
<div class="row mb-4">
|
<div class="row mb-4">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<h5 class="border-bottom pb-2">Emergency Contact</h5>
|
<h5 class="border-bottom pb-2">Address</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>
|
</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="col-md-6">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="insurance_provider" class="form-label">Insurance Provider</label>
|
<label for="{{ form.address_line_1.id_for_label }}" class="form-label">Address Line 1</label>
|
||||||
<input type="text" class="form-control" id="insurance_provider" name="insurance_provider">
|
{{ 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>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="insurance_policy_number" class="form-label">Policy Number</label>
|
<label for="{{ form.address_line_2.id_for_label }}" class="form-label">Address Line 2</label>
|
||||||
<input type="text" class="form-control" id="insurance_policy_number" name="insurance_policy_number">
|
{{ 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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Consent Forms -->
|
<!-- Other -->
|
||||||
{% if consent_templates %}
|
|
||||||
<div class="row mb-4">
|
<div class="row mb-4">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<h5 class="border-bottom pb-2">Consent Forms</h5>
|
<h5 class="border-bottom pb-2">Other</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>
|
</div>
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<!-- Submit Buttons -->
|
<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="row">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="d-flex justify-content-end gap-2">
|
<div class="d-flex justify-content-end gap-2">
|
||||||
<button type="button" class="btn btn-secondary">Cancel</button>
|
<a href="{% url 'patients:patient_list' %}" class="btn btn-secondary">Cancel</a>
|
||||||
<button type="submit" class="btn btn-primary">
|
<button type="submit" class="btn btn-primary">
|
||||||
<i class="fas fa-save me-1"></i>Register Patient
|
<i class="fas fa-save me-1"></i>Register Patient
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div> <!-- /panel-body -->
|
||||||
</div>
|
</div> <!-- /panel -->
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block js %}
|
{% block js %}{% endblock %}
|
||||||
<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 %}
|
|
||||||
|
|
||||||
@ -382,7 +382,7 @@
|
|||||||
<div class="text-center text-muted py-3">
|
<div class="text-center text-muted py-3">
|
||||||
<i class="fas fa-shield-alt fa-2x mb-2"></i>
|
<i class="fas fa-shield-alt fa-2x mb-2"></i>
|
||||||
<p class="small">No insurance information</p>
|
<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
|
Add Insurance
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -19,6 +19,7 @@
|
|||||||
</a>
|
</a>
|
||||||
<button type="button" class="btn btn-outline-secondary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown">
|
<button type="button" class="btn btn-outline-secondary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown">
|
||||||
<span class="visually-hidden">Toggle Dropdown</span>
|
<span class="visually-hidden">Toggle Dropdown</span>
|
||||||
|
<i class="fas fa-ellipsis-v"></i>
|
||||||
</button>
|
</button>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li><a class="dropdown-item" href="#" onclick="exportPatients()">
|
<li><a class="dropdown-item" href="#" onclick="exportPatients()">
|
||||||
@ -232,7 +233,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<span class="badge bg-primary">{{ patient.medical_record_number }}</span>
|
<span class="badge bg-primary">{{ patient.mrn }}</span>
|
||||||
{% if patient.ssn %}
|
{% if patient.ssn %}
|
||||||
<div class="small text-muted">SSN: ***-**-{{ patient.ssn|slice:"-4:" }}</div>
|
<div class="small text-muted">SSN: ***-**-{{ patient.ssn|slice:"-4:" }}</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -284,7 +285,7 @@
|
|||||||
title="View Profile">
|
title="View Profile">
|
||||||
<i class="fas fa-eye"></i>
|
<i class="fas fa-eye"></i>
|
||||||
</a>
|
</a>
|
||||||
<a href="{% url 'patients:patient_form' patient.pk %}"
|
<a href="{% url 'patients:patient_update' patient.pk %}"
|
||||||
class="btn btn-outline-secondary"
|
class="btn btn-outline-secondary"
|
||||||
title="Edit">
|
title="Edit">
|
||||||
<i class="fas fa-edit"></i>
|
<i class="fas fa-edit"></i>
|
||||||
@ -295,7 +296,7 @@
|
|||||||
title="Quick Actions">
|
title="Quick Actions">
|
||||||
<i class="fas fa-bolt"></i>
|
<i class="fas fa-bolt"></i>
|
||||||
</button>
|
</button>
|
||||||
<a href="{% url 'patients:patient_confirm_delete' patient.pk %}"
|
<a href="{% url 'patients:patient_delete' patient.pk %}"
|
||||||
class="btn btn-outline-danger"
|
class="btn btn-outline-danger"
|
||||||
title="Delete">
|
title="Delete">
|
||||||
<i class="fas fa-trash"></i>
|
<i class="fas fa-trash"></i>
|
||||||
@ -310,38 +311,7 @@
|
|||||||
|
|
||||||
<!-- Pagination -->
|
<!-- Pagination -->
|
||||||
{% if is_paginated %}
|
{% if is_paginated %}
|
||||||
<div class="card-footer">
|
{% include 'partial/pagination.html' %}
|
||||||
<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>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<!-- Bulk Actions -->
|
<!-- Bulk Actions -->
|
||||||
@ -477,31 +447,31 @@ function quickActions(patientId) {
|
|||||||
|
|
||||||
function scheduleAppointment() {
|
function scheduleAppointment() {
|
||||||
if (currentPatientId) {
|
if (currentPatientId) {
|
||||||
window.location.href = `{% url 'appointments:appointment_form' %}?patient=${currentPatientId}`;
|
window.location.href = `{% url 'appointments:appointment_create' %}?patient=${currentPatientId}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function newEncounter() {
|
function newEncounter() {
|
||||||
if (currentPatientId) {
|
if (currentPatientId) {
|
||||||
window.location.href = `{% url 'emr:encounter_form' %}?patient=${currentPatientId}`;
|
window.location.href = `{% url 'emr:encounter_create' %}?patient=${currentPatientId}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function orderLabTest() {
|
function orderLabTest() {
|
||||||
if (currentPatientId) {
|
if (currentPatientId) {
|
||||||
window.location.href = `{% url 'laboratory:lab_order_form' %}?patient=${currentPatientId}`;
|
window.location.href = `{% url 'laboratory:lab_order_create' %}?patient=${currentPatientId}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function newPrescription() {
|
function newPrescription() {
|
||||||
if (currentPatientId) {
|
if (currentPatientId) {
|
||||||
window.location.href = `{% url 'pharmacy:prescription_form' %}?patient=${currentPatientId}`;
|
window.location.href = `{% url 'pharmacy:prescription_create' %}?patient=${currentPatientId}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function addNote() {
|
function addNote() {
|
||||||
if (currentPatientId) {
|
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