339 lines
14 KiB
HTML
339 lines
14 KiB
HTML
{% extends "layouts/base.html" %}
|
|
{% load i18n %}
|
|
|
|
{% block title %}{% trans "Send Survey Manually" %} - PX360{% 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-0">
|
|
<i class="bi bi-send me-2"></i>
|
|
{% trans "Send Survey Manually" %}
|
|
</h1>
|
|
<p class="text-muted mb-0">
|
|
{% trans "Select a survey template and send it to a patient or staff member" %}
|
|
</p>
|
|
</div>
|
|
<a href="{% url 'surveys:instance_list' %}" class="btn btn-outline-secondary">
|
|
<i class="bi bi-arrow-left me-2"></i>
|
|
{% trans "Back to Surveys" %}
|
|
</a>
|
|
</div>
|
|
|
|
<!-- Form Card -->
|
|
<div class="card shadow-sm">
|
|
<div class="card-header bg-white">
|
|
<h5 class="card-title mb-0">
|
|
<i class="bi bi-envelope-paper me-2"></i>
|
|
{% trans "Survey Details" %}
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<form method="post" id="manualSurveySendForm">
|
|
{% csrf_token %}
|
|
|
|
<!-- Recipient Type -->
|
|
<div class="mb-4">
|
|
<label class="form-label fw-bold">
|
|
{% trans "Recipient Type" %}
|
|
<span class="text-danger">*</span>
|
|
</label>
|
|
<div class="row">
|
|
<div class="col-md-3">
|
|
<div class="form-check form-check-inline">
|
|
<input class="form-check-input" type="radio"
|
|
name="recipient_type"
|
|
id="recipient_type_patient"
|
|
value="patient"
|
|
{% if form.recipient_type.value == 'patient' or not form.recipient_type.value %}checked{% endif %}>
|
|
<label class="form-check-label" for="recipient_type_patient">
|
|
<i class="bi bi-person me-1"></i>
|
|
{% trans "Patient" %}
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="form-check form-check-inline">
|
|
<input class="form-check-input" type="radio"
|
|
name="recipient_type"
|
|
id="recipient_type_staff"
|
|
value="staff"
|
|
{% if form.recipient_type.value == 'staff' %}checked{% endif %}>
|
|
<label class="form-check-label" for="recipient_type_staff">
|
|
<i class="bi bi-person-workspace me-1"></i>
|
|
{% trans "Staff" %}
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Survey Template -->
|
|
<div class="mb-4">
|
|
<label for="{{ form.survey_template.id_for_label }}" class="form-label fw-bold">
|
|
{% trans "Survey Template" %}
|
|
<span class="text-danger">*</span>
|
|
</label>
|
|
{{ form.survey_template }}
|
|
{% if form.survey_template.errors %}
|
|
<div class="invalid-feedback d-block">
|
|
{% for error in form.survey_template.errors %}{{ error }}{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Recipient Search -->
|
|
<div class="mb-4">
|
|
<label for="{{ form.recipient.id_for_label }}" class="form-label fw-bold">
|
|
{% trans "Recipient" %}
|
|
<span class="text-danger">*</span>
|
|
</label>
|
|
<div class="position-relative">
|
|
<input type="text"
|
|
class="form-control"
|
|
id="recipient_search"
|
|
placeholder="{% trans 'Search by name, MRN, or ID...' %}"
|
|
autocomplete="off">
|
|
<div id="recipient_id" style="display: none;">{{ form.recipient.value }}</div>
|
|
<div class="invalid-feedback d-block" id="recipient_error" style="display: none;">
|
|
{% trans "Please select a recipient from the search results" %}
|
|
</div>
|
|
</div>
|
|
<div class="form-text">
|
|
<i class="bi bi-info-circle me-1"></i>
|
|
{% trans "Start typing to search. Select a recipient from the dropdown." %}
|
|
</div>
|
|
|
|
<!-- Selected Recipient Display -->
|
|
<div id="selected_recipient" class="mt-2" style="display: none;">
|
|
<div class="alert alert-info d-flex align-items-center">
|
|
<i class="bi bi-person-check fs-4 me-3"></i>
|
|
<div>
|
|
<strong id="selected_recipient_name"></strong>
|
|
<div id="selected_recipient_details" class="small text-muted"></div>
|
|
</div>
|
|
<button type="button" class="btn btn-sm btn-outline-danger ms-auto" id="clear_recipient">
|
|
<i class="bi bi-x-lg"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Search Results Dropdown -->
|
|
<div id="search_results" class="position-absolute w-100 bg-white border rounded shadow-sm mt-1"
|
|
style="max-height: 300px; overflow-y: auto; z-index: 1000; display: none;">
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Delivery Channel -->
|
|
<div class="mb-4">
|
|
<label for="{{ form.delivery_channel.id_for_label }}" class="form-label fw-bold">
|
|
{% trans "Delivery Channel" %}
|
|
<span class="text-danger">*</span>
|
|
</label>
|
|
<div class="input-group">
|
|
{{ form.delivery_channel }}
|
|
</div>
|
|
{% if form.delivery_channel.errors %}
|
|
<div class="invalid-feedback d-block">
|
|
{% for error in form.delivery_channel.errors %}{{ error }}{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Custom Message -->
|
|
<div class="mb-4">
|
|
<label for="{{ form.custom_message.id_for_label }}" class="form-label fw-bold">
|
|
{% trans "Custom Message" %}
|
|
<span class="text-muted">({% trans "Optional" %})</span>
|
|
</label>
|
|
{{ form.custom_message }}
|
|
{% if form.custom_message.errors %}
|
|
<div class="invalid-feedback d-block">
|
|
{% for error in form.custom_message.errors %}{{ error }}{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
<div class="form-text">
|
|
<i class="bi bi-info-circle me-1"></i>
|
|
{% trans "Add a personalized message to the survey invitation" %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Submit Buttons -->
|
|
<div class="d-flex justify-content-end gap-2">
|
|
<a href="{% url 'surveys:instance_list' %}" class="btn btn-outline-secondary">
|
|
{% trans "Cancel" %}
|
|
</a>
|
|
<button type="submit" class="btn btn-primary" id="submitBtn">
|
|
<i class="bi bi-send me-2"></i>
|
|
{% trans "Send Survey" %}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Hidden form field for actual submission -->
|
|
<input type="hidden" name="recipient" id="recipient_input" value="">
|
|
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const searchInput = document.getElementById('recipient_search');
|
|
const searchResults = document.getElementById('search_results');
|
|
const recipientInput = document.getElementById('recipient_input');
|
|
const selectedRecipientDiv = document.getElementById('selected_recipient');
|
|
const selectedRecipientName = document.getElementById('selected_recipient_name');
|
|
const selectedRecipientDetails = document.getElementById('selected_recipient_details');
|
|
const recipientError = document.getElementById('recipient_error');
|
|
const clearRecipientBtn = document.getElementById('clear_recipient');
|
|
const recipientTypeRadios = document.getElementsByName('recipient_type');
|
|
|
|
let searchTimeout;
|
|
let selectedRecipient = null;
|
|
|
|
// Recipient type change handler
|
|
recipientTypeRadios.forEach(radio => {
|
|
radio.addEventListener('change', function() {
|
|
clearRecipient();
|
|
searchInput.focus();
|
|
});
|
|
});
|
|
|
|
// Search functionality
|
|
searchInput.addEventListener('input', function() {
|
|
clearTimeout(searchTimeout);
|
|
const query = this.value.trim();
|
|
|
|
if (query.length < 2) {
|
|
searchResults.style.display = 'none';
|
|
return;
|
|
}
|
|
|
|
searchTimeout = setTimeout(function() {
|
|
performSearch(query);
|
|
}, 300);
|
|
});
|
|
|
|
// Hide search results when clicking outside
|
|
document.addEventListener('click', function(e) {
|
|
if (!searchResults.contains(e.target) && e.target !== searchInput) {
|
|
searchResults.style.display = 'none';
|
|
}
|
|
});
|
|
|
|
// Clear recipient button
|
|
clearRecipientBtn.addEventListener('click', clearRecipient);
|
|
|
|
function clearRecipient() {
|
|
selectedRecipient = null;
|
|
recipientInput.value = '';
|
|
searchInput.value = '';
|
|
selectedRecipientDiv.style.display = 'none';
|
|
recipientError.style.display = 'none';
|
|
}
|
|
|
|
function performSearch(query) {
|
|
const recipientType = document.querySelector('input[name="recipient_type"]:checked').value;
|
|
const searchUrl = `/api/${recipientType}s/search/?q=${encodeURIComponent(query)}&active=true`;
|
|
|
|
fetch(searchUrl)
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
displaySearchResults(data);
|
|
})
|
|
.catch(error => {
|
|
console.error('Search error:', error);
|
|
});
|
|
}
|
|
|
|
function displaySearchResults(results) {
|
|
if (!results || results.length === 0) {
|
|
searchResults.innerHTML = `
|
|
<div class="p-3 text-muted text-center">
|
|
<i class="bi bi-search me-2"></i>
|
|
{% trans "No results found" %}
|
|
</div>
|
|
`;
|
|
} else {
|
|
const recipientType = document.querySelector('input[name="recipient_type"]:checked').value;
|
|
searchResults.innerHTML = results.map(result => {
|
|
let details = '';
|
|
if (recipientType === 'patient') {
|
|
details = result.mrn ? `MRN: ${result.mrn}` : '';
|
|
} else {
|
|
details = result.employee_id ? `ID: ${result.employee_id}` : '';
|
|
}
|
|
return `
|
|
<div class="p-3 border-bottom cursor-pointer hover-bg-light"
|
|
style="cursor: pointer;"
|
|
data-id="${result.id}"
|
|
data-name="${result.first_name} ${result.last_name}"
|
|
data-details="${details}">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<div>
|
|
<strong>${result.first_name} ${result.last_name}</strong>
|
|
${details ? `<div class="small text-muted">${details}</div>` : ''}
|
|
</div>
|
|
<i class="bi bi-plus-circle text-primary"></i>
|
|
</div>
|
|
</div>
|
|
`;
|
|
}).join('');
|
|
|
|
// Add click handlers to results
|
|
searchResults.querySelectorAll('[data-id]').forEach(item => {
|
|
item.addEventListener('click', function() {
|
|
selectRecipient(this.dataset.id, this.dataset.name, this.dataset.details);
|
|
});
|
|
});
|
|
}
|
|
|
|
searchResults.style.display = 'block';
|
|
}
|
|
|
|
function selectRecipient(id, name, details) {
|
|
selectedRecipient = { id, name, details };
|
|
recipientInput.value = id;
|
|
searchInput.value = name;
|
|
|
|
selectedRecipientName.textContent = name;
|
|
selectedRecipientDetails.textContent = details || '';
|
|
selectedRecipientDiv.style.display = 'block';
|
|
searchResults.style.display = 'none';
|
|
recipientError.style.display = 'none';
|
|
}
|
|
|
|
// Form submission validation
|
|
const form = document.getElementById('manualSurveySendForm');
|
|
form.addEventListener('submit', function(e) {
|
|
if (!selectedRecipient || !selectedRecipient.id) {
|
|
e.preventDefault();
|
|
recipientError.style.display = 'block';
|
|
searchInput.classList.add('is-invalid');
|
|
return false;
|
|
}
|
|
|
|
recipientInput.value = selectedRecipient.id;
|
|
|
|
// Show loading state
|
|
const submitBtn = document.getElementById('submitBtn');
|
|
submitBtn.disabled = true;
|
|
submitBtn.innerHTML = `
|
|
<span class="spinner-border spinner-border-sm me-2"></span>
|
|
{% trans "Sending..." %}
|
|
`;
|
|
});
|
|
});
|
|
</script>
|
|
|
|
<style>
|
|
.hover-bg-light:hover {
|
|
background-color: #f8f9fa;
|
|
}
|
|
.cursor-pointer {
|
|
cursor: pointer;
|
|
}
|
|
</style>
|
|
{% endblock %} |