948 lines
51 KiB
HTML
948 lines
51 KiB
HTML
{% extends "base.html" %}
|
|
{% load static %}
|
|
|
|
{% block title %}Notification Preferences - {{ block.super }}{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="container-fluid">
|
|
<!-- Page Header -->
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<div>
|
|
<h1 class="h3 mb-1">Notification Preferences</h1>
|
|
<nav aria-label="breadcrumb">
|
|
<ol class="breadcrumb mb-0">
|
|
<li class="breadcrumb-item"><a href="{% url 'communications:dashboard' %}">Communications</a></li>
|
|
<li class="breadcrumb-item"><a href="{% url 'communications:notification_list' %}">Notifications</a></li>
|
|
<li class="breadcrumb-item active">Preferences</li>
|
|
</ol>
|
|
</nav>
|
|
</div>
|
|
<div class="btn-group">
|
|
<a href="{% url 'communications:notification_settings' %}" class="btn btn-outline-secondary">
|
|
<i class="fas fa-cog me-2"></i>General Settings
|
|
</a>
|
|
<a href="{% url 'communications:notification_list' %}" class="btn btn-outline-primary">
|
|
<i class="fas fa-arrow-left me-2"></i>Back to Notifications
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<!-- Main Content -->
|
|
<div class="col-lg-8">
|
|
<!-- Notification Categories -->
|
|
<div class="card mb-4">
|
|
<div class="card-header">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-layer-group me-2"></i>Notification Categories
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="table-responsive">
|
|
<table class="table table-hover">
|
|
<thead>
|
|
<tr>
|
|
<th>Category</th>
|
|
<th class="text-center">Email</th>
|
|
<th class="text-center">SMS</th>
|
|
<th class="text-center">Push</th>
|
|
<th class="text-center">In-App</th>
|
|
<th class="text-center">Priority</th>
|
|
<th class="text-center">Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<!-- Appointments -->
|
|
<tr>
|
|
<td>
|
|
<div class="d-flex align-items-center">
|
|
<i class="fas fa-calendar-check text-primary me-3"></i>
|
|
<div>
|
|
<div class="fw-bold">Appointments</div>
|
|
<div class="small text-muted">Reminders, confirmations, cancellations</div>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td class="text-center">
|
|
<div class="form-check form-switch d-flex justify-content-center">
|
|
<input class="form-check-input" type="checkbox" id="appointments_email" checked onchange="updatePreference('appointments', 'email', this.checked)">
|
|
</div>
|
|
</td>
|
|
<td class="text-center">
|
|
<div class="form-check form-switch d-flex justify-content-center">
|
|
<input class="form-check-input" type="checkbox" id="appointments_sms" checked onchange="updatePreference('appointments', 'sms', this.checked)">
|
|
</div>
|
|
</td>
|
|
<td class="text-center">
|
|
<div class="form-check form-switch d-flex justify-content-center">
|
|
<input class="form-check-input" type="checkbox" id="appointments_push" checked onchange="updatePreference('appointments', 'push', this.checked)">
|
|
</div>
|
|
</td>
|
|
<td class="text-center">
|
|
<div class="form-check form-switch d-flex justify-content-center">
|
|
<input class="form-check-input" type="checkbox" id="appointments_in_app" checked onchange="updatePreference('appointments', 'in_app', this.checked)">
|
|
</div>
|
|
</td>
|
|
<td class="text-center">
|
|
<select class="form-select form-select-sm" onchange="updatePreference('appointments', 'priority', this.value)">
|
|
<option value="HIGH" selected>High</option>
|
|
<option value="NORMAL">Normal</option>
|
|
<option value="LOW">Low</option>
|
|
</select>
|
|
</td>
|
|
<td class="text-center">
|
|
<button type="button" class="btn btn-outline-primary btn-sm" onclick="customizeCategory('appointments')">
|
|
<i class="fas fa-cog"></i>
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
|
|
<!-- Lab Results -->
|
|
<tr>
|
|
<td>
|
|
<div class="d-flex align-items-center">
|
|
<i class="fas fa-flask text-success me-3"></i>
|
|
<div>
|
|
<div class="fw-bold">Lab Results</div>
|
|
<div class="small text-muted">Test results, reports available</div>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td class="text-center">
|
|
<div class="form-check form-switch d-flex justify-content-center">
|
|
<input class="form-check-input" type="checkbox" id="lab_results_email" checked onchange="updatePreference('lab_results', 'email', this.checked)">
|
|
</div>
|
|
</td>
|
|
<td class="text-center">
|
|
<div class="form-check form-switch d-flex justify-content-center">
|
|
<input class="form-check-input" type="checkbox" id="lab_results_sms" onchange="updatePreference('lab_results', 'sms', this.checked)">
|
|
</div>
|
|
</td>
|
|
<td class="text-center">
|
|
<div class="form-check form-switch d-flex justify-content-center">
|
|
<input class="form-check-input" type="checkbox" id="lab_results_push" checked onchange="updatePreference('lab_results', 'push', this.checked)">
|
|
</div>
|
|
</td>
|
|
<td class="text-center">
|
|
<div class="form-check form-switch d-flex justify-content-center">
|
|
<input class="form-check-input" type="checkbox" id="lab_results_in_app" checked onchange="updatePreference('lab_results', 'in_app', this.checked)">
|
|
</div>
|
|
</td>
|
|
<td class="text-center">
|
|
<select class="form-select form-select-sm" onchange="updatePreference('lab_results', 'priority', this.value)">
|
|
<option value="HIGH" selected>High</option>
|
|
<option value="NORMAL">Normal</option>
|
|
<option value="LOW">Low</option>
|
|
</select>
|
|
</td>
|
|
<td class="text-center">
|
|
<button type="button" class="btn btn-outline-primary btn-sm" onclick="customizeCategory('lab_results')">
|
|
<i class="fas fa-cog"></i>
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
|
|
<!-- Medications -->
|
|
<tr>
|
|
<td>
|
|
<div class="d-flex align-items-center">
|
|
<i class="fas fa-pills text-warning me-3"></i>
|
|
<div>
|
|
<div class="fw-bold">Medications</div>
|
|
<div class="small text-muted">Reminders, refill alerts, interactions</div>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td class="text-center">
|
|
<div class="form-check form-switch d-flex justify-content-center">
|
|
<input class="form-check-input" type="checkbox" id="medications_email" onchange="updatePreference('medications', 'email', this.checked)">
|
|
</div>
|
|
</td>
|
|
<td class="text-center">
|
|
<div class="form-check form-switch d-flex justify-content-center">
|
|
<input class="form-check-input" type="checkbox" id="medications_sms" checked onchange="updatePreference('medications', 'sms', this.checked)">
|
|
</div>
|
|
</td>
|
|
<td class="text-center">
|
|
<div class="form-check form-switch d-flex justify-content-center">
|
|
<input class="form-check-input" type="checkbox" id="medications_push" checked onchange="updatePreference('medications', 'push', this.checked)">
|
|
</div>
|
|
</td>
|
|
<td class="text-center">
|
|
<div class="form-check form-switch d-flex justify-content-center">
|
|
<input class="form-check-input" type="checkbox" id="medications_in_app" checked onchange="updatePreference('medications', 'in_app', this.checked)">
|
|
</div>
|
|
</td>
|
|
<td class="text-center">
|
|
<select class="form-select form-select-sm" onchange="updatePreference('medications', 'priority', this.value)">
|
|
<option value="HIGH" selected>High</option>
|
|
<option value="NORMAL">Normal</option>
|
|
<option value="LOW">Low</option>
|
|
</select>
|
|
</td>
|
|
<td class="text-center">
|
|
<button type="button" class="btn btn-outline-primary btn-sm" onclick="customizeCategory('medications')">
|
|
<i class="fas fa-cog"></i>
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
|
|
<!-- Billing -->
|
|
<tr>
|
|
<td>
|
|
<div class="d-flex align-items-center">
|
|
<i class="fas fa-file-invoice text-info me-3"></i>
|
|
<div>
|
|
<div class="fw-bold">Billing & Payments</div>
|
|
<div class="small text-muted">Bills, payment due, receipts</div>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td class="text-center">
|
|
<div class="form-check form-switch d-flex justify-content-center">
|
|
<input class="form-check-input" type="checkbox" id="billing_email" checked onchange="updatePreference('billing', 'email', this.checked)">
|
|
</div>
|
|
</td>
|
|
<td class="text-center">
|
|
<div class="form-check form-switch d-flex justify-content-center">
|
|
<input class="form-check-input" type="checkbox" id="billing_sms" onchange="updatePreference('billing', 'sms', this.checked)">
|
|
</div>
|
|
</td>
|
|
<td class="text-center">
|
|
<div class="form-check form-switch d-flex justify-content-center">
|
|
<input class="form-check-input" type="checkbox" id="billing_push" onchange="updatePreference('billing', 'push', this.checked)">
|
|
</div>
|
|
</td>
|
|
<td class="text-center">
|
|
<div class="form-check form-switch d-flex justify-content-center">
|
|
<input class="form-check-input" type="checkbox" id="billing_in_app" checked onchange="updatePreference('billing', 'in_app', this.checked)">
|
|
</div>
|
|
</td>
|
|
<td class="text-center">
|
|
<select class="form-select form-select-sm" onchange="updatePreference('billing', 'priority', this.value)">
|
|
<option value="HIGH">High</option>
|
|
<option value="NORMAL" selected>Normal</option>
|
|
<option value="LOW">Low</option>
|
|
</select>
|
|
</td>
|
|
<td class="text-center">
|
|
<button type="button" class="btn btn-outline-primary btn-sm" onclick="customizeCategory('billing')">
|
|
<i class="fas fa-cog"></i>
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
|
|
<!-- Emergency -->
|
|
<tr class="table-danger">
|
|
<td>
|
|
<div class="d-flex align-items-center">
|
|
<i class="fas fa-exclamation-triangle text-danger me-3"></i>
|
|
<div>
|
|
<div class="fw-bold">Emergency Alerts</div>
|
|
<div class="small text-muted">Critical alerts, emergency notifications</div>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td class="text-center">
|
|
<div class="form-check form-switch d-flex justify-content-center">
|
|
<input class="form-check-input" type="checkbox" id="emergency_email" checked disabled>
|
|
</div>
|
|
</td>
|
|
<td class="text-center">
|
|
<div class="form-check form-switch d-flex justify-content-center">
|
|
<input class="form-check-input" type="checkbox" id="emergency_sms" checked disabled>
|
|
</div>
|
|
</td>
|
|
<td class="text-center">
|
|
<div class="form-check form-switch d-flex justify-content-center">
|
|
<input class="form-check-input" type="checkbox" id="emergency_push" checked disabled>
|
|
</div>
|
|
</td>
|
|
<td class="text-center">
|
|
<div class="form-check form-switch d-flex justify-content-center">
|
|
<input class="form-check-input" type="checkbox" id="emergency_in_app" checked disabled>
|
|
</div>
|
|
</td>
|
|
<td class="text-center">
|
|
<select class="form-select form-select-sm" disabled>
|
|
<option value="CRITICAL" selected>Critical</option>
|
|
</select>
|
|
</td>
|
|
<td class="text-center">
|
|
<span class="badge bg-danger">Required</span>
|
|
</td>
|
|
</tr>
|
|
|
|
<!-- System -->
|
|
<tr>
|
|
<td>
|
|
<div class="d-flex align-items-center">
|
|
<i class="fas fa-cogs text-secondary me-3"></i>
|
|
<div>
|
|
<div class="fw-bold">System Updates</div>
|
|
<div class="small text-muted">Maintenance, updates, announcements</div>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td class="text-center">
|
|
<div class="form-check form-switch d-flex justify-content-center">
|
|
<input class="form-check-input" type="checkbox" id="system_email" onchange="updatePreference('system', 'email', this.checked)">
|
|
</div>
|
|
</td>
|
|
<td class="text-center">
|
|
<div class="form-check form-switch d-flex justify-content-center">
|
|
<input class="form-check-input" type="checkbox" id="system_sms" onchange="updatePreference('system', 'sms', this.checked)">
|
|
</div>
|
|
</td>
|
|
<td class="text-center">
|
|
<div class="form-check form-switch d-flex justify-content-center">
|
|
<input class="form-check-input" type="checkbox" id="system_push" onchange="updatePreference('system', 'push', this.checked)">
|
|
</div>
|
|
</td>
|
|
<td class="text-center">
|
|
<div class="form-check form-switch d-flex justify-content-center">
|
|
<input class="form-check-input" type="checkbox" id="system_in_app" checked onchange="updatePreference('system', 'in_app', this.checked)">
|
|
</div>
|
|
</td>
|
|
<td class="text-center">
|
|
<select class="form-select form-select-sm" onchange="updatePreference('system', 'priority', this.value)">
|
|
<option value="HIGH">High</option>
|
|
<option value="NORMAL">Normal</option>
|
|
<option value="LOW" selected>Low</option>
|
|
</select>
|
|
</td>
|
|
<td class="text-center">
|
|
<button type="button" class="btn btn-outline-primary btn-sm" onclick="customizeCategory('system')">
|
|
<i class="fas fa-cog"></i>
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
|
|
<!-- Clinical -->
|
|
<tr>
|
|
<td>
|
|
<div class="d-flex align-items-center">
|
|
<i class="fas fa-stethoscope text-primary me-3"></i>
|
|
<div>
|
|
<div class="fw-bold">Clinical Updates</div>
|
|
<div class="small text-muted">Care plans, treatment updates, consultations</div>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td class="text-center">
|
|
<div class="form-check form-switch d-flex justify-content-center">
|
|
<input class="form-check-input" type="checkbox" id="clinical_email" checked onchange="updatePreference('clinical', 'email', this.checked)">
|
|
</div>
|
|
</td>
|
|
<td class="text-center">
|
|
<div class="form-check form-switch d-flex justify-content-center">
|
|
<input class="form-check-input" type="checkbox" id="clinical_sms" onchange="updatePreference('clinical', 'sms', this.checked)">
|
|
</div>
|
|
</td>
|
|
<td class="text-center">
|
|
<div class="form-check form-switch d-flex justify-content-center">
|
|
<input class="form-check-input" type="checkbox" id="clinical_push" checked onchange="updatePreference('clinical', 'push', this.checked)">
|
|
</div>
|
|
</td>
|
|
<td class="text-center">
|
|
<div class="form-check form-switch d-flex justify-content-center">
|
|
<input class="form-check-input" type="checkbox" id="clinical_in_app" checked onchange="updatePreference('clinical', 'in_app', this.checked)">
|
|
</div>
|
|
</td>
|
|
<td class="text-center">
|
|
<select class="form-select form-select-sm" onchange="updatePreference('clinical', 'priority', this.value)">
|
|
<option value="HIGH" selected>High</option>
|
|
<option value="NORMAL">Normal</option>
|
|
<option value="LOW">Low</option>
|
|
</select>
|
|
</td>
|
|
<td class="text-center">
|
|
<button type="button" class="btn btn-outline-primary btn-sm" onclick="customizeCategory('clinical')">
|
|
<i class="fas fa-cog"></i>
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
|
|
<!-- Administrative -->
|
|
<tr>
|
|
<td>
|
|
<div class="d-flex align-items-center">
|
|
<i class="fas fa-clipboard-list text-muted me-3"></i>
|
|
<div>
|
|
<div class="fw-bold">Administrative</div>
|
|
<div class="small text-muted">Forms, documents, policy updates</div>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td class="text-center">
|
|
<div class="form-check form-switch d-flex justify-content-center">
|
|
<input class="form-check-input" type="checkbox" id="administrative_email" onchange="updatePreference('administrative', 'email', this.checked)">
|
|
</div>
|
|
</td>
|
|
<td class="text-center">
|
|
<div class="form-check form-switch d-flex justify-content-center">
|
|
<input class="form-check-input" type="checkbox" id="administrative_sms" onchange="updatePreference('administrative', 'sms', this.checked)">
|
|
</div>
|
|
</td>
|
|
<td class="text-center">
|
|
<div class="form-check form-switch d-flex justify-content-center">
|
|
<input class="form-check-input" type="checkbox" id="administrative_push" onchange="updatePreference('administrative', 'push', this.checked)">
|
|
</div>
|
|
</td>
|
|
<td class="text-center">
|
|
<div class="form-check form-switch d-flex justify-content-center">
|
|
<input class="form-check-input" type="checkbox" id="administrative_in_app" checked onchange="updatePreference('administrative', 'in_app', this.checked)">
|
|
</div>
|
|
</td>
|
|
<td class="text-center">
|
|
<select class="form-select form-select-sm" onchange="updatePreference('administrative', 'priority', this.value)">
|
|
<option value="HIGH">High</option>
|
|
<option value="NORMAL">Normal</option>
|
|
<option value="LOW" selected>Low</option>
|
|
</select>
|
|
</td>
|
|
<td class="text-center">
|
|
<button type="button" class="btn btn-outline-primary btn-sm" onclick="customizeCategory('administrative')">
|
|
<i class="fas fa-cog"></i>
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
<div class="card-footer">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<div class="text-muted small">
|
|
<i class="fas fa-info-circle me-1"></i>
|
|
Emergency notifications cannot be disabled for safety reasons.
|
|
</div>
|
|
<div class="btn-group">
|
|
<button type="button" class="btn btn-outline-success btn-sm" onclick="enableAllCategories()">
|
|
<i class="fas fa-check-double me-1"></i>Enable All
|
|
</button>
|
|
<button type="button" class="btn btn-outline-warning btn-sm" onclick="resetToDefaults()">
|
|
<i class="fas fa-undo me-1"></i>Reset Defaults
|
|
</button>
|
|
<button type="button" class="btn btn-success btn-sm" onclick="savePreferences()">
|
|
<i class="fas fa-save me-1"></i>Save Changes
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Custom Timing Rules -->
|
|
<div class="card mb-4">
|
|
<div class="card-header">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-clock me-2"></i>Custom Timing Rules
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<h6 class="mb-3">Appointment Reminders</h6>
|
|
<div class="form-check mb-2">
|
|
<input class="form-check-input" type="checkbox" id="reminder_24h" checked>
|
|
<label class="form-check-label" for="reminder_24h">
|
|
24 hours before
|
|
</label>
|
|
</div>
|
|
<div class="form-check mb-2">
|
|
<input class="form-check-input" type="checkbox" id="reminder_2h" checked>
|
|
<label class="form-check-label" for="reminder_2h">
|
|
2 hours before
|
|
</label>
|
|
</div>
|
|
<div class="form-check mb-2">
|
|
<input class="form-check-input" type="checkbox" id="reminder_30m">
|
|
<label class="form-check-label" for="reminder_30m">
|
|
30 minutes before
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<h6 class="mb-3">Medication Reminders</h6>
|
|
<div class="form-check mb-2">
|
|
<input class="form-check-input" type="checkbox" id="med_reminder_time" checked>
|
|
<label class="form-check-label" for="med_reminder_time">
|
|
At scheduled time
|
|
</label>
|
|
</div>
|
|
<div class="form-check mb-2">
|
|
<input class="form-check-input" type="checkbox" id="med_reminder_15m">
|
|
<label class="form-check-label" for="med_reminder_15m">
|
|
15 minutes after missed dose
|
|
</label>
|
|
</div>
|
|
<div class="form-check mb-2">
|
|
<input class="form-check-input" type="checkbox" id="med_reminder_refill" checked>
|
|
<label class="form-check-label" for="med_reminder_refill">
|
|
3 days before refill needed
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Sidebar -->
|
|
<div class="col-lg-4">
|
|
<!-- Preference Summary -->
|
|
<div class="card mb-3">
|
|
<div class="card-header">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-chart-pie me-2"></i>Preference Summary
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="mb-3">
|
|
<div class="d-flex justify-content-between mb-2">
|
|
<span>Email Notifications:</span>
|
|
<span class="badge bg-primary" id="email-count">4/8</span>
|
|
</div>
|
|
<div class="progress mb-3" style="height: 6px;">
|
|
<div class="progress-bar bg-primary" style="width: 50%" id="email-progress"></div>
|
|
</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<div class="d-flex justify-content-between mb-2">
|
|
<span>SMS Notifications:</span>
|
|
<span class="badge bg-success" id="sms-count">3/8</span>
|
|
</div>
|
|
<div class="progress mb-3" style="height: 6px;">
|
|
<div class="progress-bar bg-success" style="width: 37.5%" id="sms-progress"></div>
|
|
</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<div class="d-flex justify-content-between mb-2">
|
|
<span>Push Notifications:</span>
|
|
<span class="badge bg-info" id="push-count">5/8</span>
|
|
</div>
|
|
<div class="progress mb-3" style="height: 6px;">
|
|
<div class="progress-bar bg-info" style="width: 62.5%" id="push-progress"></div>
|
|
</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<div class="d-flex justify-content-between mb-2">
|
|
<span>In-App Notifications:</span>
|
|
<span class="badge bg-warning" id="in-app-count">7/8</span>
|
|
</div>
|
|
<div class="progress" style="height: 6px;">
|
|
<div class="progress-bar bg-warning" style="width: 87.5%" id="in-app-progress"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Quick Actions -->
|
|
<div class="card mb-3">
|
|
<div class="card-header">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-bolt me-2"></i>Quick Actions
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="d-grid gap-2">
|
|
<button type="button" class="btn btn-outline-primary" onclick="enableAllCategories()">
|
|
<i class="fas fa-check-double me-2"></i>Enable All Categories
|
|
</button>
|
|
<button type="button" class="btn btn-outline-warning" onclick="enableEssentialOnly()">
|
|
<i class="fas fa-exclamation-triangle me-2"></i>Essential Only
|
|
</button>
|
|
<button type="button" class="btn btn-outline-info" onclick="enableWorkHours()">
|
|
<i class="fas fa-business-time me-2"></i>Work Hours Only
|
|
</button>
|
|
<button type="button" class="btn btn-outline-secondary" onclick="disableNonCritical()">
|
|
<i class="fas fa-volume-mute me-2"></i>Disable Non-Critical
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Recent Changes -->
|
|
<div class="card mb-3">
|
|
<div class="card-header">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-history me-2"></i>Recent Changes
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div id="recent-changes">
|
|
<div class="small text-muted text-center">No recent changes</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Help -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-question-circle me-2"></i>Help
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="accordion accordion-flush" id="helpAccordion">
|
|
<div class="accordion-item">
|
|
<h2 class="accordion-header">
|
|
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#help1">
|
|
Channel Types
|
|
</button>
|
|
</h2>
|
|
<div id="help1" class="accordion-collapse collapse" data-bs-parent="#helpAccordion">
|
|
<div class="accordion-body small">
|
|
<strong>Email:</strong> Detailed notifications sent to your email address.<br>
|
|
<strong>SMS:</strong> Brief text messages to your phone.<br>
|
|
<strong>Push:</strong> Mobile app notifications.<br>
|
|
<strong>In-App:</strong> Notifications within the web application.
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="accordion-item">
|
|
<h2 class="accordion-header">
|
|
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#help2">
|
|
Priority Levels
|
|
</button>
|
|
</h2>
|
|
<div id="help2" class="accordion-collapse collapse" data-bs-parent="#helpAccordion">
|
|
<div class="accordion-body small">
|
|
<strong>Critical:</strong> Emergency alerts that override all settings.<br>
|
|
<strong>High:</strong> Important notifications that may override quiet hours.<br>
|
|
<strong>Normal:</strong> Standard notifications.<br>
|
|
<strong>Low:</strong> Non-urgent notifications that respect quiet hours.
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Category Customization Modal -->
|
|
<div class="modal fade" id="categoryCustomizationModal" tabindex="-1">
|
|
<div class="modal-dialog modal-lg">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Customize Category</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body" id="categoryCustomizationContent">
|
|
<!-- Content will be loaded dynamically -->
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="button" class="btn btn-primary" onclick="saveCategoryCustomization()">Save Changes</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
let preferences = {};
|
|
let recentChanges = [];
|
|
|
|
// Initialize preferences page
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
loadCurrentPreferences();
|
|
updateSummary();
|
|
});
|
|
|
|
function loadCurrentPreferences() {
|
|
fetch('{% url "communications:notification_preferences_api" %}')
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
preferences = data.preferences;
|
|
updateFormFromPreferences();
|
|
})
|
|
.catch(error => console.error('Error loading preferences:', error));
|
|
}
|
|
|
|
function updateFormFromPreferences() {
|
|
Object.keys(preferences).forEach(category => {
|
|
const categoryPrefs = preferences[category];
|
|
|
|
Object.keys(categoryPrefs).forEach(channel => {
|
|
const checkbox = document.getElementById(`${category}_${channel}`);
|
|
if (checkbox && checkbox.type === 'checkbox') {
|
|
checkbox.checked = categoryPrefs[channel];
|
|
} else if (checkbox && checkbox.tagName === 'SELECT') {
|
|
checkbox.value = categoryPrefs[channel];
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
function updatePreference(category, channel, value) {
|
|
if (!preferences[category]) {
|
|
preferences[category] = {};
|
|
}
|
|
|
|
preferences[category][channel] = value;
|
|
|
|
// Add to recent changes
|
|
const change = {
|
|
timestamp: new Date(),
|
|
category: category,
|
|
channel: channel,
|
|
value: value,
|
|
action: value ? 'enabled' : 'disabled'
|
|
};
|
|
|
|
recentChanges.unshift(change);
|
|
if (recentChanges.length > 5) {
|
|
recentChanges.pop();
|
|
}
|
|
|
|
updateSummary();
|
|
updateRecentChanges();
|
|
|
|
// Auto-save after a short delay
|
|
clearTimeout(window.autoSaveTimeout);
|
|
window.autoSaveTimeout = setTimeout(savePreferences, 2000);
|
|
}
|
|
|
|
function updateSummary() {
|
|
const channels = ['email', 'sms', 'push', 'in_app'];
|
|
const totalCategories = Object.keys(preferences).length || 8;
|
|
|
|
channels.forEach(channel => {
|
|
let enabledCount = 0;
|
|
|
|
Object.keys(preferences).forEach(category => {
|
|
if (preferences[category] && preferences[category][channel]) {
|
|
enabledCount++;
|
|
}
|
|
});
|
|
|
|
const percentage = (enabledCount / totalCategories) * 100;
|
|
|
|
const countElement = document.getElementById(`${channel.replace('_', '-')}-count`);
|
|
const progressElement = document.getElementById(`${channel.replace('_', '-')}-progress`);
|
|
|
|
if (countElement) {
|
|
countElement.textContent = `${enabledCount}/${totalCategories}`;
|
|
}
|
|
|
|
if (progressElement) {
|
|
progressElement.style.width = `${percentage}%`;
|
|
}
|
|
});
|
|
}
|
|
|
|
function updateRecentChanges() {
|
|
const container = document.getElementById('recent-changes');
|
|
|
|
if (recentChanges.length === 0) {
|
|
container.innerHTML = '<div class="small text-muted text-center">No recent changes</div>';
|
|
return;
|
|
}
|
|
|
|
const changesHtml = recentChanges.map(change => {
|
|
const timeAgo = getTimeAgo(change.timestamp);
|
|
const icon = change.value ? 'fa-check text-success' : 'fa-times text-danger';
|
|
|
|
return `
|
|
<div class="d-flex align-items-center mb-2">
|
|
<i class="fas ${icon} me-2"></i>
|
|
<div class="flex-grow-1">
|
|
<div class="small fw-bold">${change.category.replace('_', ' ').toUpperCase()}</div>
|
|
<div class="small text-muted">${change.channel.toUpperCase()} ${change.action}</div>
|
|
</div>
|
|
<div class="small text-muted">${timeAgo}</div>
|
|
</div>
|
|
`;
|
|
}).join('');
|
|
|
|
container.innerHTML = changesHtml;
|
|
}
|
|
|
|
function getTimeAgo(timestamp) {
|
|
const now = new Date();
|
|
const diff = now - timestamp;
|
|
const minutes = Math.floor(diff / 60000);
|
|
|
|
if (minutes < 1) return 'Just now';
|
|
if (minutes < 60) return `${minutes}m ago`;
|
|
|
|
const hours = Math.floor(minutes / 60);
|
|
if (hours < 24) return `${hours}h ago`;
|
|
|
|
const days = Math.floor(hours / 24);
|
|
return `${days}d ago`;
|
|
}
|
|
|
|
function enableAllCategories() {
|
|
const categories = ['appointments', 'lab_results', 'medications', 'billing', 'system', 'clinical', 'administrative'];
|
|
const channels = ['email', 'sms', 'push', 'in_app'];
|
|
|
|
categories.forEach(category => {
|
|
channels.forEach(channel => {
|
|
const checkbox = document.getElementById(`${category}_${channel}`);
|
|
if (checkbox && !checkbox.disabled) {
|
|
checkbox.checked = true;
|
|
updatePreference(category, channel, true);
|
|
}
|
|
});
|
|
});
|
|
|
|
showToast('Success', 'All categories enabled', 'success');
|
|
}
|
|
|
|
function enableEssentialOnly() {
|
|
const essentialSettings = {
|
|
appointments: { email: true, sms: true, push: true, in_app: true },
|
|
lab_results: { email: true, push: true, in_app: true },
|
|
medications: { sms: true, push: true, in_app: true },
|
|
emergency: { email: true, sms: true, push: true, in_app: true }
|
|
};
|
|
|
|
// First disable all
|
|
disableAllCategories();
|
|
|
|
// Then enable essential
|
|
Object.keys(essentialSettings).forEach(category => {
|
|
Object.keys(essentialSettings[category]).forEach(channel => {
|
|
const checkbox = document.getElementById(`${category}_${channel}`);
|
|
if (checkbox && !checkbox.disabled) {
|
|
checkbox.checked = essentialSettings[category][channel];
|
|
updatePreference(category, channel, essentialSettings[category][channel]);
|
|
}
|
|
});
|
|
});
|
|
|
|
showToast('Success', 'Essential notifications enabled', 'success');
|
|
}
|
|
|
|
function enableWorkHours() {
|
|
// This would typically integrate with quiet hours settings
|
|
enableEssentialOnly();
|
|
showToast('Info', 'Work hours preset applied. Configure quiet hours in settings for full effect.', 'info');
|
|
}
|
|
|
|
function disableNonCritical() {
|
|
const categories = ['system', 'administrative', 'billing'];
|
|
const channels = ['email', 'sms', 'push', 'in_app'];
|
|
|
|
categories.forEach(category => {
|
|
channels.forEach(channel => {
|
|
const checkbox = document.getElementById(`${category}_${channel}`);
|
|
if (checkbox && !checkbox.disabled) {
|
|
checkbox.checked = false;
|
|
updatePreference(category, channel, false);
|
|
}
|
|
});
|
|
});
|
|
|
|
showToast('Success', 'Non-critical notifications disabled', 'success');
|
|
}
|
|
|
|
function disableAllCategories() {
|
|
const categories = ['appointments', 'lab_results', 'medications', 'billing', 'system', 'clinical', 'administrative'];
|
|
const channels = ['email', 'sms', 'push', 'in_app'];
|
|
|
|
categories.forEach(category => {
|
|
channels.forEach(channel => {
|
|
const checkbox = document.getElementById(`${category}_${channel}`);
|
|
if (checkbox && !checkbox.disabled) {
|
|
checkbox.checked = false;
|
|
updatePreference(category, channel, false);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
function resetToDefaults() {
|
|
if (confirm('Reset all preferences to default settings?')) {
|
|
fetch('{% url "communications:reset_notification_preferences" %}', {
|
|
method: 'POST',
|
|
headers: {
|
|
'X-CSRFToken': getCsrfToken()
|
|
}
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
loadCurrentPreferences();
|
|
showToast('Success', 'Preferences reset to defaults', 'success');
|
|
} else {
|
|
showToast('Error', data.error || 'Failed to reset preferences', 'error');
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Error resetting preferences:', error);
|
|
showToast('Error', 'Failed to reset preferences', 'error');
|
|
});
|
|
}
|
|
}
|
|
|
|
function customizeCategory(category) {
|
|
fetch(`{% url 'communications:category_customization' 'CATEGORY' %}`.replace('CATEGORY', category))
|
|
.then(response => response.text())
|
|
.then(html => {
|
|
document.getElementById('categoryCustomizationContent').innerHTML = html;
|
|
const modal = new bootstrap.Modal(document.getElementById('categoryCustomizationModal'));
|
|
modal.show();
|
|
})
|
|
.catch(error => {
|
|
console.error('Error loading category customization:', error);
|
|
showToast('Error', 'Failed to load customization options', 'error');
|
|
});
|
|
}
|
|
|
|
function saveCategoryCustomization() {
|
|
// Implementation would depend on the specific customization form
|
|
bootstrap.Modal.getInstance(document.getElementById('categoryCustomizationModal')).hide();
|
|
showToast('Success', 'Category customization saved', 'success');
|
|
}
|
|
|
|
function savePreferences() {
|
|
fetch('{% url "communications:save_notification_preferences" %}', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRFToken': getCsrfToken()
|
|
},
|
|
body: JSON.stringify({
|
|
preferences: preferences
|
|
})
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
showToast('Success', 'Preferences saved successfully', 'success');
|
|
} else {
|
|
showToast('Error', data.error || 'Failed to save preferences', 'error');
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Error saving preferences:', error);
|
|
showToast('Error', 'Failed to save preferences', 'error');
|
|
});
|
|
}
|
|
|
|
function getCsrfToken() {
|
|
return document.querySelector('[name=csrfmiddlewaretoken]').value;
|
|
}
|
|
|
|
function showToast(title, message, type) {
|
|
// Implementation depends on your toast system
|
|
console.log(`${type.toUpperCase()}: ${title} - ${message}`);
|
|
}
|
|
|
|
// Auto-update summary when checkboxes change
|
|
document.addEventListener('change', function(e) {
|
|
if (e.target.type === 'checkbox' && e.target.id.includes('_')) {
|
|
updateSummary();
|
|
}
|
|
});
|
|
|
|
// Update recent changes display every minute
|
|
setInterval(updateRecentChanges, 60000);
|
|
</script>
|
|
{% endblock %}
|
|
|