kaauh_ats/templates/forms/form_submissions.html
2025-10-05 12:19:45 +03:00

429 lines
19 KiB
HTML

{% extends "base.html" %}
{% block title %}{{ form.title }} - Submissions{% endblock %}
{% block content %}
<div class="container-fluid">
<div class="row">
<div class="col-12">
<div class="d-flex justify-content-between align-items-center mb-4">
<div>
<h1><i class="fas fa-list"></i> Form Submissions</h1>
<p class="text-muted mb-0">{{ form.title }}</p>
</div>
<div>
<a href="{% url 'form_preview' form.id %}" class="btn btn-outline-primary me-2" target="_blank">
<i class="fas fa-eye"></i> Preview Form
</a>
<a href="{% url 'form_list' %}" class="btn btn-outline-secondary">
<i class="fas fa-arrow-left"></i> Back to Forms
</a>
</div>
</div>
<!-- Statistics Cards -->
<div class="row mb-4">
<div class="col-md-3">
<div class="card bg-primary text-white">
<div class="card-body">
<div class="d-flex justify-content-between">
<div>
<h4 class="mb-0">{{ page_obj.paginator.count }}</h4>
<small>Total Submissions</small>
</div>
<div class="align-self-center">
<i class="fas fa-users fa-2x opacity-75"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card bg-success text-white">
<div class="card-body">
<div class="d-flex justify-content-between">
<div>
<h4 class="mb-0">{{ form.submissions.all|length }}</h4>
<small>All Time</small>
</div>
<div class="align-self-center">
<i class="fas fa-chart-line fa-2x opacity-75"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card bg-info text-white">
<div class="card-body">
<div class="d-flex justify-content-between">
<div>
<h4 class="mb-0">
{% if form.submissions.first %}
{{ form.submissions.first.submitted_at|timesince }}
{% else %}
No submissions
{% endif %}
</h4>
<small>Latest</small>
</div>
<div class="align-self-center">
<i class="fas fa-clock fa-2x opacity-75"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card bg-warning text-white">
<div class="card-body">
<div class="d-flex justify-content-between">
<div>
<h4 class="mb-0">{{ form.created_at|date:"M d" }}</h4>
<small>Form Created</small>
</div>
<div class="align-self-center">
<i class="fas fa-calendar fa-2x opacity-75"></i>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Export Options -->
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0"><i class="fas fa-download"></i> Export Options</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<button class="btn btn-outline-primary me-2" onclick="exportSubmissions('csv')">
<i class="fas fa-file-csv"></i> Export as CSV
</button>
<button class="btn btn-outline-success me-2" onclick="exportSubmissions('excel')">
<i class="fas fa-file-excel"></i> Export as Excel
</button>
<button class="btn btn-outline-secondary" onclick="exportSubmissions('json')">
<i class="fas fa-file-code"></i> Export as JSON
</button>
</div>
<div class="col-md-6 text-end">
<small class="text-muted">
Download all submission data for analysis
</small>
</div>
</div>
</div>
</div>
<!-- Submissions List -->
{% if page_obj %}
<div class="card">
<div class="card-header">
<h5 class="mb-0">Recent Submissions</h5>
</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-hover mb-0">
<thead class="table-light">
<tr>
<th>#</th>
<th>Submitted</th>
<th>IP Address</th>
<th>User Agent</th>
<th>Data Fields</th>
<th>Files</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for submission in page_obj %}
<tr>
<td>
<span class="badge bg-primary">{{ submission.id }}</span>
</td>
<td>
<div>
<strong>{{ submission.submitted_at|date:"M d, Y" }}</strong><br>
<small class="text-muted">{{ submission.submitted_at|time:"g:i A" }}</small>
</div>
</td>
<td>
<code class="text-muted">{{ submission.ip_address|default:"N/A" }}</code>
</td>
<td>
<small class="text-muted" title="{{ submission.user_agent }}">
{% if submission.user_agent %}
{{ submission.user_agent|truncatechars:50 }}
{% else %}
N/A
{% endif %}
</small>
</td>
<td>
<span class="badge bg-info">{{ submission.submission_data.keys|length }} fields</span>
</td>
<td>
<span class="badge bg-success">{{ submission.files.count }} files</span>
</td>
<td>
<div class="btn-group btn-group-sm">
<button type="button" class="btn btn-outline-primary" onclick="viewSubmission({{ submission.id }})" title="View Details">
<i class="fas fa-eye"></i>
</button>
<button type="button" class="btn btn-outline-info" onclick="downloadSubmission({{ submission.id }})" title="Download">
<i class="fas fa-download"></i>
</button>
<button type="button" class="btn btn-outline-danger" onclick="deleteSubmission({{ submission.id }})" title="Delete">
<i class="fas fa-trash"></i>
</button>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
<!-- Pagination -->
{% if page_obj.has_other_pages %}
<nav aria-label="Submissions pagination" class="mt-4">
<ul class="pagination justify-content-center">
{% if page_obj.has_previous %}
<li class="page-item">
<a class="page-link" href="?page=1">First</a>
</li>
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.previous_page_number }}">Previous</a>
</li>
{% endif %}
<li class="page-item active">
<span class="page-link">{{ page_obj.number }} of {{ page_obj.paginator.num_pages }}</span>
</li>
{% if page_obj.has_next %}
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.next_page_number }}">Next</a>
</li>
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.paginator.num_pages }}">Last</a>
</li>
{% endif %}
</ul>
</nav>
{% endif %}
{% else %}
<div class="card">
<div class="card-body text-center py-5">
<i class="fas fa-inbox fa-3x text-muted mb-3"></i>
<h4>No submissions yet</h4>
<p class="text-muted">This form hasn't received any submissions yet.</p>
<a href="{% url 'form_preview' form.id %}" class="btn btn-primary" target="_blank">
<i class="fas fa-external-link-alt"></i> Test Form
</a>
</div>
</div>
{% endif %}
</div>
</div>
</div>
<!-- Submission Detail Modal -->
<div class="modal fade" id="submissionModal" tabindex="-1" aria-labelledby="submissionModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="submissionModalLabel">
<i class="fas fa-file-alt"></i> Submission Details
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div id="submissionDetails">
<!-- Submission details will be loaded here -->
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary" id="downloadSubmissionBtn">
<i class="fas fa-download"></i> Download
</button>
</div>
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
<script>
let currentSubmissionId = null;
function viewSubmission(submissionId) {
currentSubmissionId = submissionId;
fetch(`/recruitment/api/submissions/${submissionId}/`)
.then(response => response.json())
.then(data => {
if (data.success) {
displaySubmissionDetails(data.submission);
const modal = new bootstrap.Modal(document.getElementById('submissionModal'));
modal.show();
} else {
alert('Error loading submission: ' + data.error);
}
})
.catch(error => {
console.error('Error:', error);
alert('Error loading submission details');
});
}
function displaySubmissionDetails(submission) {
const detailsHtml = `
<div class="row">
<div class="col-md-6">
<h6>Submission Information</h6>
<table class="table table-sm">
<tr>
<td><strong>ID:</strong></td>
<td>${submission.id}</td>
</tr>
<tr>
<td><strong>Submitted:</strong></td>
<td>${new Date(submission.submitted_at).toLocaleString()}</td>
</tr>
<tr>
<td><strong>IP Address:</strong></td>
<td>${submission.ip_address || 'N/A'}</td>
</tr>
</table>
</div>
<div class="col-md-6">
<h6>Technical Details</h6>
<table class="table table-sm">
<tr>
<td><strong>User Agent:</strong></td>
<td><small>${submission.user_agent || 'N/A'}</small></td>
</tr>
<tr>
<td><strong>Files:</strong></td>
<td>${submission.files ? submission.files.length : 0}</td>
</tr>
<tr>
<td><strong>Data Fields:</strong></td>
<td>${Object.keys(submission.submission_data).length}</td>
</tr>
</table>
</div>
</div>
<hr>
<h6>Submitted Data</h6>
<div class="table-responsive">
<table class="table table-sm">
<thead>
<tr>
<th>Field</th>
<th>Value</th>
</tr>
</thead>
<tbody>
${Object.entries(submission.submission_data).map(([key, value]) => `
<tr>
<td><strong>${key}:</strong></td>
<td>${formatFieldValue(value)}</td>
</tr>
`).join('')}
</tbody>
</table>
</div>
${submission.files && submission.files.length > 0 ? `
<hr>
<h6>Uploaded Files</h6>
<div class="list-group">
${submission.files.map(file => `
<div class="list-group-item d-flex justify-content-between align-items-center">
<div>
<i class="fas fa-file me-2"></i>
<strong>${file.original_filename}</strong>
<br>
<small class="text-muted">
${file.file_size ? formatFileSize(file.file_size) : 'Unknown size'}
Uploaded ${new Date(file.uploaded_at).toLocaleString()}
</small>
</div>
<a href="${file.file_url}" class="btn btn-sm btn-outline-primary" target="_blank">
<i class="fas fa-download"></i> Download
</a>
</div>
`).join('')}
</div>
` : ''}
`;
document.getElementById('submissionDetails').innerHTML = detailsHtml;
document.getElementById('downloadSubmissionBtn').onclick = () => downloadSubmission(submission.id);
}
function formatFieldValue(value) {
if (Array.isArray(value)) {
return value.join(', ');
}
if (typeof value === 'object' && value !== null) {
return JSON.stringify(value);
}
return value || 'N/A';
}
function formatFileSize(bytes) {
if (!bytes) return 'Unknown';
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(1024));
return Math.round(bytes / Math.pow(1024, i) * 100) / 100 + ' ' + sizes[i];
}
function downloadSubmission(submissionId) {
window.open(`/recruitment/api/submissions/${submissionId}/download/`, '_blank');
}
function deleteSubmission(submissionId) {
if (confirm('Are you sure you want to delete this submission? This action cannot be undone.')) {
fetch(`/recruitment/api/submissions/${submissionId}/delete/`, {
method: 'DELETE',
headers: {
'X-CSRFToken': getCsrfToken()
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
location.reload();
} else {
alert('Error deleting submission: ' + data.error);
}
})
.catch(error => {
console.error('Error:', error);
alert('Error deleting submission');
});
}
}
function exportSubmissions(format) {
window.open(`/recruitment/api/forms/{{ form.id }}/export/?format=${format}`, '_blank');
}
function getCsrfToken() {
const cookie = document.cookie.split(';').find(c => c.trim().startsWith('csrftoken='));
return cookie ? cookie.split('=')[1] : '';
}
</script>
{% endblock %}