Spaces:
Sleeping
Sleeping
{% extends "base.html" %} | |
{% block title %}SMS Logs - Admin Portal{% endblock %} | |
{% block content %} | |
<div class="container-fluid py-4"> | |
<div class="row"> | |
<div class="col-12"> | |
<div class="d-flex justify-content-between align-items-center mb-4"> | |
<h2><i class="fas fa-sms text-success me-3"></i>SMS Logs</h2> | |
<div> | |
<a href="{{ url_for('admin_dashboard') }}" class="btn btn-outline-secondary me-2"> | |
<i class="fas fa-arrow-left me-2"></i>Back to Dashboard | |
</a> | |
<button class="btn btn-success" onclick="sendTestSMS()"> | |
<i class="fas fa-paper-plane me-2"></i>Send Test SMS | |
</button> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- SMS Statistics --> | |
<div class="row mb-4"> | |
<div class="col-md-3"> | |
<div class="card bg-primary text-white"> | |
<div class="card-body text-center"> | |
<h3>{{ total_sms }}</h3> | |
<p class="mb-0">Total SMS Sent</p> | |
</div> | |
</div> | |
</div> | |
<div class="col-md-3"> | |
<div class="card bg-success text-white"> | |
<div class="card-body text-center"> | |
<h3>{{ delivered_sms }}</h3> | |
<p class="mb-0">Delivered</p> | |
</div> | |
</div> | |
</div> | |
<div class="col-md-3"> | |
<div class="card bg-warning text-white"> | |
<div class="card-body text-center"> | |
<h3>{{ pending_sms }}</h3> | |
<p class="mb-0">Pending</p> | |
</div> | |
</div> | |
</div> | |
<div class="col-md-3"> | |
<div class="card bg-danger text-white"> | |
<div class="card-body text-center"> | |
<h3>{{ failed_sms }}</h3> | |
<p class="mb-0">Failed</p> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- SMS Logs Table --> | |
<div class="row"> | |
<div class="col-12"> | |
<div class="card shadow"> | |
<div class="card-header bg-success text-white"> | |
<h5 class="mb-0"><i class="fas fa-list me-2"></i>SMS History</h5> | |
</div> | |
<div class="card-body"> | |
<!-- Filter Controls --> | |
<div class="row mb-3"> | |
<div class="col-md-3"> | |
<select class="form-select" id="statusFilter"> | |
<option value="">All Status</option> | |
<option value="sent">Sent</option> | |
<option value="delivered">Delivered</option> | |
<option value="failed">Failed</option> | |
<option value="pending">Pending</option> | |
</select> | |
</div> | |
<div class="col-md-3"> | |
<input type="date" class="form-control" id="dateFilter" placeholder="Filter by date"> | |
</div> | |
<div class="col-md-4"> | |
<input type="text" class="form-control" id="phoneFilter" placeholder="Search by phone number"> | |
</div> | |
<div class="col-md-2"> | |
<button class="btn btn-primary" onclick="applyFilters()"> | |
<i class="fas fa-filter me-2"></i>Filter | |
</button> | |
</div> | |
</div> | |
<div class="table-responsive"> | |
<table class="table table-striped" id="smsLogsTable"> | |
<thead> | |
<tr> | |
<th>ID</th> | |
<th>Farmer</th> | |
<th>Phone</th> | |
<th>Message Type</th> | |
<th>Content</th> | |
<th>Status</th> | |
<th>Sent At</th> | |
<th>Delivered At</th> | |
<th>Actions</th> | |
</tr> | |
</thead> | |
<tbody> | |
{% for sms in sms_logs %} | |
<tr> | |
<td>{{ sms.id }}</td> | |
<td>{{ sms.farmer.name if sms.farmer else 'N/A' }}</td> | |
<td>{{ sms.phone_number }}</td> | |
<td> | |
<span class="badge bg-info">{{ sms.message_type }}</span> | |
</td> | |
<td> | |
<div class="text-truncate" style="max-width: 200px;" title="{{ sms.message_content }}"> | |
{{ sms.message_content }} | |
</div> | |
</td> | |
<td> | |
{% if sms.delivery_status == 'delivered' %} | |
<span class="badge bg-success">Delivered</span> | |
{% elif sms.delivery_status == 'failed' %} | |
<span class="badge bg-danger">Failed</span> | |
{% elif sms.delivery_status == 'pending' %} | |
<span class="badge bg-warning">Pending</span> | |
{% else %} | |
<span class="badge bg-primary">Sent</span> | |
{% endif %} | |
</td> | |
<td>{{ sms.sent_at.strftime('%Y-%m-%d %H:%M') if sms.sent_at else 'N/A' }}</td> | |
<td>{{ sms.delivered_at.strftime('%Y-%m-%d %H:%M') if sms.delivered_at else 'N/A' }}</td> | |
<td> | |
<div class="btn-group" role="group"> | |
<button class="btn btn-sm btn-primary" onclick="viewSMS({{ sms.id }})"> | |
<i class="fas fa-eye"></i> | |
</button> | |
{% if sms.delivery_status in ['failed', 'pending'] %} | |
<button class="btn btn-sm btn-warning" onclick="retrySMS({{ sms.id }})"> | |
<i class="fas fa-redo"></i> | |
</button> | |
{% endif %} | |
</div> | |
</td> | |
</tr> | |
{% endfor %} | |
</tbody> | |
</table> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- View SMS Modal --> | |
<div class="modal fade" id="viewSMSModal" tabindex="-1"> | |
<div class="modal-dialog modal-lg"> | |
<div class="modal-content"> | |
<div class="modal-header"> | |
<h5 class="modal-title"><i class="fas fa-sms me-2"></i>SMS Details</h5> | |
<button type="button" class="btn-close" data-bs-dismiss="modal"></button> | |
</div> | |
<div class="modal-body" id="smsDetailsContent"> | |
<!-- SMS details will be loaded here --> | |
</div> | |
<div class="modal-footer"> | |
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- Send Test SMS Modal --> | |
<div class="modal fade" id="testSMSModal" tabindex="-1"> | |
<div class="modal-dialog"> | |
<div class="modal-content"> | |
<div class="modal-header"> | |
<h5 class="modal-title"><i class="fas fa-paper-plane me-2"></i>Send Test SMS</h5> | |
<button type="button" class="btn-close" data-bs-dismiss="modal"></button> | |
</div> | |
<form id="testSMSForm"> | |
<div class="modal-body"> | |
<div class="mb-3"> | |
<label for="testPhone" class="form-label">Phone Number *</label> | |
<input type="tel" class="form-control" id="testPhone" required> | |
</div> | |
<div class="mb-3"> | |
<label for="testMessage" class="form-label">Message *</label> | |
<textarea class="form-control" id="testMessage" rows="4" maxlength="160" required placeholder="Enter your test message here..."></textarea> | |
<div class="form-text"> | |
<span id="charCount">0</span>/160 characters | |
</div> | |
</div> | |
</div> | |
<div class="modal-footer"> | |
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button> | |
<button type="submit" class="btn btn-success"> | |
<i class="fas fa-paper-plane me-2"></i>Send SMS | |
</button> | |
</div> | |
</form> | |
</div> | |
</div> | |
</div> | |
<script> | |
// Initialize DataTable | |
$(document).ready(function() { | |
$('#smsLogsTable').DataTable({ | |
order: [[0, 'desc']], | |
pageLength: 25, | |
responsive: true | |
}); | |
// Character counter for test message | |
$('#testMessage').on('input', function() { | |
const length = $(this).val().length; | |
$('#charCount').text(length); | |
if (length > 160) { | |
$('#charCount').addClass('text-danger'); | |
} else { | |
$('#charCount').removeClass('text-danger'); | |
} | |
}); | |
}); | |
// Apply filters | |
function applyFilters() { | |
const status = $('#statusFilter').val(); | |
const date = $('#dateFilter').val(); | |
const phone = $('#phoneFilter').val(); | |
const params = new URLSearchParams(); | |
if (status) params.append('status', status); | |
if (date) params.append('date', date); | |
if (phone) params.append('phone', phone); | |
window.location.href = `{{ url_for('admin_sms_logs') }}?${params.toString()}`; | |
} | |
// View SMS Details | |
function viewSMS(smsId) { | |
$.ajax({ | |
url: `/admin/sms/${smsId}`, | |
method: 'GET', | |
success: function(sms) { | |
const content = ` | |
<div class="row"> | |
<div class="col-md-6"> | |
<h6>Message Information</h6> | |
<p><strong>ID:</strong> ${sms.id}</p> | |
<p><strong>Type:</strong> ${sms.message_type}</p> | |
<p><strong>Phone:</strong> ${sms.phone_number}</p> | |
<p><strong>Farmer:</strong> ${sms.farmer ? sms.farmer.name : 'N/A'}</p> | |
</div> | |
<div class="col-md-6"> | |
<h6>Delivery Status</h6> | |
<p><strong>Status:</strong> | |
<span class="badge bg-${sms.delivery_status === 'delivered' ? 'success' : | |
sms.delivery_status === 'failed' ? 'danger' : | |
sms.delivery_status === 'pending' ? 'warning' : 'primary'}"> | |
${sms.delivery_status} | |
</span> | |
</p> | |
<p><strong>Sent At:</strong> ${sms.sent_at ? new Date(sms.sent_at).toLocaleString() : 'N/A'}</p> | |
<p><strong>Delivered At:</strong> ${sms.delivered_at ? new Date(sms.delivered_at).toLocaleString() : 'N/A'}</p> | |
<p><strong>Error Message:</strong> ${sms.error_message || 'None'}</p> | |
</div> | |
</div> | |
<hr> | |
<h6>Message Content</h6> | |
<div class="bg-light p-3 rounded"> | |
${sms.message_content} | |
</div> | |
`; | |
$('#smsDetailsContent').html(content); | |
$('#viewSMSModal').modal('show'); | |
}, | |
error: function() { | |
showToast('Error loading SMS details', 'error'); | |
} | |
}); | |
} | |
// Retry SMS | |
function retrySMS(smsId) { | |
if (confirm('Are you sure you want to retry sending this SMS?')) { | |
$.ajax({ | |
url: `/admin/sms/${smsId}/retry`, | |
method: 'POST', | |
success: function(response) { | |
if (response.success) { | |
showToast('SMS retry initiated successfully!', 'success'); | |
location.reload(); | |
} else { | |
showToast(response.message || 'Error retrying SMS', 'error'); | |
} | |
}, | |
error: function() { | |
showToast('Error retrying SMS', 'error'); | |
} | |
}); | |
} | |
} | |
// Send Test SMS | |
function sendTestSMS() { | |
$('#testSMSModal').modal('show'); | |
} | |
// Test SMS Form | |
$('#testSMSForm').on('submit', function(e) { | |
e.preventDefault(); | |
const formData = { | |
phone_number: $('#testPhone').val(), | |
message: $('#testMessage').val() | |
}; | |
$.ajax({ | |
url: '/admin/send_test_sms', | |
method: 'POST', | |
contentType: 'application/json', | |
data: JSON.stringify(formData), | |
success: function(response) { | |
if (response.success) { | |
showToast('Test SMS sent successfully!', 'success'); | |
$('#testSMSModal').modal('hide'); | |
$('#testSMSForm')[0].reset(); | |
$('#charCount').text('0'); | |
setTimeout(() => location.reload(), 2000); | |
} else { | |
showToast(response.message || 'Error sending test SMS', 'error'); | |
} | |
}, | |
error: function() { | |
showToast('Error sending test SMS', 'error'); | |
} | |
}); | |
}); | |
// Toast notification function | |
function showToast(message, type) { | |
const toast = document.createElement('div'); | |
toast.className = `alert alert-${type === 'success' ? 'success' : type === 'error' ? 'danger' : 'info'} alert-dismissible fade show position-fixed`; | |
toast.style.top = '20px'; | |
toast.style.right = '20px'; | |
toast.style.zIndex = '9999'; | |
toast.innerHTML = ` | |
${message} | |
<button type="button" class="btn-close" data-bs-dismiss="alert"></button> | |
`; | |
document.body.appendChild(toast); | |
setTimeout(() => { | |
toast.remove(); | |
}, 5000); | |
} | |
</script> | |
{% endblock %} | |