423 lines
18 KiB
HTML
423 lines
18 KiB
HTML
{% extends "base.html" %}
|
|
{% load form_filters %}
|
|
|
|
{% block title %}{{ form.name }} - Submission Details{% 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-file-alt"></i> Submission Details</h1>
|
|
<p class="text-muted mb-0">{{ form.name }}</p>
|
|
</div>
|
|
<div>
|
|
<a href="" class="btn btn-outline-primary me-2">
|
|
<i class="fas fa-arrow-left"></i> Back to Submissions
|
|
</a>
|
|
<a href="" class="btn btn-outline-primary me-2" target="_blank">
|
|
<i class="fas fa-eye"></i> Preview Form
|
|
</a>
|
|
<button class="btn btn-success" onclick="exportSubmission()">
|
|
<i class="fas fa-download"></i> Export
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Submission Summary -->
|
|
<div class="row mb-4">
|
|
<div class="col-md-4">
|
|
<div class="card bg-primary text-white">
|
|
<div class="card-body">
|
|
<div class="d-flex justify-content-between">
|
|
<div>
|
|
<h4 class="mb-0">{{ submission.id }}</h4>
|
|
<small>Submission ID</small>
|
|
</div>
|
|
<div class="align-self-center">
|
|
<i class="fas fa-hashtag fa-2x opacity-75"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="card bg-success text-white">
|
|
<div class="card-body">
|
|
<div class="d-flex justify-content-between">
|
|
<div>
|
|
<h4 class="mb-0">{{ submission.submitted_at|date:"M d, Y" }}</h4>
|
|
<small>Submitted</small>
|
|
</div>
|
|
<div class="align-self-center">
|
|
<i class="fas fa-calendar-check fa-2x opacity-75"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="card bg-info text-white">
|
|
<div class="card-body">
|
|
<div class="d-flex justify-content-between">
|
|
<div>
|
|
<h4 class="mb-0">{{ responses|length }}</h4>
|
|
<small>Fields Completed</small>
|
|
</div>
|
|
<div class="align-self-center">
|
|
<i class="fas fa-tasks fa-2x opacity-75"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Submission Information -->
|
|
<div class="card mb-4">
|
|
<div class="card-header">
|
|
<h5 class="mb-0"><i class="fas fa-info-circle"></i> Submission Information</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<table class="table table-sm">
|
|
<tr>
|
|
<td><strong>Form:</strong></td>
|
|
<td>{{ form.name }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong>Submitted:</strong></td>
|
|
<td>{{ submission.submitted_at|date:"F d, Y H:i" }}</td>
|
|
</tr>
|
|
{% if submission.submitted_by %}
|
|
<tr>
|
|
<td><strong>Submitted By:</strong></td>
|
|
<td>{{ submission.submitted_by.get_full_name|default:submission.submitted_by.username }}</td>
|
|
</tr>
|
|
{% endif %}
|
|
</table>
|
|
</div>
|
|
<div class="col-md-6">
|
|
{% if submission.applicant_name %}
|
|
<table class="table table-sm">
|
|
<tr>
|
|
<td><strong>Applicant Name:</strong></td>
|
|
<td>{{ submission.applicant_name }}</td>
|
|
</tr>
|
|
{% endif %}
|
|
{% if submission.applicant_email %}
|
|
<tr>
|
|
<td><strong>Email:</strong></td>
|
|
<td>{{ submission.applicant_email }}</td>
|
|
</tr>
|
|
{% endif %}
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Form Responses by Stage -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="mb-0"><i class="fas fa-clipboard-list"></i> Submitted Responses</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
{% for stage in stages %}
|
|
<div class="mb-4">
|
|
<div class="card border-primary">
|
|
<div class="card-header bg-primary text-white">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-layer-group"></i> {{ stage.name }}
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
{% get_stage_responses stage_responses stage.id as stage_data %}
|
|
{% if stage_data %}
|
|
<div class="table-responsive">
|
|
<table class="table table-striped table-hover">
|
|
<thead class="table-dark">
|
|
<tr>
|
|
<th>Field Label</th>
|
|
<th>Field Type</th>
|
|
<th>Response Value</th>
|
|
<th>File</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for response in stage_data %}
|
|
<tr>
|
|
<td>
|
|
{{ response.field.label }}
|
|
{% if response.field.required %}
|
|
<span class="text-danger">*</span>
|
|
{% endif %}
|
|
</td>
|
|
<td>{{ response.field.get_field_type_display }}</td>
|
|
<td>
|
|
{% if response.uploaded_file %}
|
|
<span class="text-primary">File: {{ response.uploaded_file.name }}</span>
|
|
{% elif response.value %}
|
|
{% if response.field.field_type == 'checkbox' and response.value|length > 0 %}
|
|
<ul class="list-unstyled mb-0">
|
|
{% for val in response.value %}
|
|
<li><i class="fas fa-check text-success"></i> {{ val }}</li>
|
|
{% endfor %}
|
|
</ul>
|
|
{% elif response.field.field_type == 'radio' %}
|
|
<span class="badge bg-info">{{ response.value }}</span>
|
|
{% elif response.field.field_type == 'select' %}
|
|
<span class="badge bg-secondary">{{ response.value }}</span>
|
|
{% else %}
|
|
<p class="mb-0">{{ response.value|linebreaksbr }}</p>
|
|
{% endif %}
|
|
{% else %}
|
|
<span class="text-muted">Not provided</span>
|
|
{% endif %}
|
|
</td>
|
|
<td>
|
|
{% if response.uploaded_file %}
|
|
<a href="{{ response.uploaded_file.url }}" class="btn btn-sm btn-outline-primary" target="_blank">
|
|
<i class="fas fa-download"></i> Download
|
|
</a>
|
|
{% endif %}
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{% else %}
|
|
<div class="text-center text-muted py-3">
|
|
<i class="fas fa-inbox fa-2x mb-2"></i>
|
|
<p>No responses submitted for this stage.</p>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% if not forloop.last %}
|
|
<hr>
|
|
{% endif %}
|
|
{% empty %}
|
|
<div class="text-center text-muted py-5">
|
|
<i class="fas fa-inbox fa-3x mb-3"></i>
|
|
<h4>No stages found</h4>
|
|
<p>This form doesn't have any stages defined.</p>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Raw Data Table -->
|
|
<div class="card mt-4">
|
|
<div class="card-header">
|
|
<h5 class="mb-0"><i class="fas fa-table"></i> All Responses (Raw Data)</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
{% get_all_responses_flat stage_responses as all_responses %}
|
|
{% if all_responses %}
|
|
<div class="table-responsive">
|
|
<table class="table table-striped table-hover">
|
|
<thead class="table-dark">
|
|
<tr>
|
|
<th>Stage</th>
|
|
<th>Field Label</th>
|
|
<th>Field Type</th>
|
|
<th>Required</th>
|
|
<th>Response Value</th>
|
|
<th>File</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for response in all_responses %}
|
|
<tr>
|
|
<td>{{ response.stage_name }}</td>
|
|
<td>
|
|
{{ response.field_label }}
|
|
{% if response.required %}
|
|
<span class="text-danger">*</span>
|
|
{% endif %}
|
|
</td>
|
|
<td>{{ response.field_type }}</td>
|
|
<td>
|
|
{% if response.required %}
|
|
<span class="badge bg-danger">Yes</span>
|
|
{% else %}
|
|
<span class="badge bg-secondary">No</span>
|
|
{% endif %}
|
|
</td>
|
|
<td>
|
|
{% if response.uploaded_file %}
|
|
<span class="text-primary">File: {{ response.uploaded_file.name }}</span>
|
|
{% elif response.value %}
|
|
{% if response.field_type == 'checkbox' and response.value|length > 0 %}
|
|
<ul class="list-unstyled mb-0">
|
|
{% for val in response.value %}
|
|
<li><i class="fas fa-check text-success"></i> {{ val }}</li>
|
|
{% endfor %}
|
|
</ul>
|
|
{% elif response.field_type == 'radio' %}
|
|
<span class="badge bg-info">{{ response.value }}</span>
|
|
{% elif response.field_type == 'select' %}
|
|
<span class="badge bg-secondary">{{ response.value }}</span>
|
|
{% else %}
|
|
<p class="mb-0">{{ response.value|linebreaksbr }}</p>
|
|
{% endif %}
|
|
{% else %}
|
|
<span class="text-muted">Not provided</span>
|
|
{% endif %}
|
|
</td>
|
|
<td>
|
|
{% if response.uploaded_file %}
|
|
<a href="{{ response.uploaded_file.url }}" class="btn btn-sm btn-outline-primary" target="_blank">
|
|
<i class="fas fa-download"></i> Download
|
|
</a>
|
|
{% endif %}
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{% else %}
|
|
<div class="text-center text-muted py-3">
|
|
<i class="fas fa-inbox fa-2x mb-2"></i>
|
|
<p>No responses found for this submission.</p>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Delete Confirmation Modal -->
|
|
<div class="modal fade" id="deleteModal" tabindex="-1" aria-labelledby="deleteModalLabel" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title" id="deleteModalLabel">
|
|
<i class="fas fa-exclamation-triangle text-danger"></i> Confirm Delete
|
|
</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<p>Are you sure you want to delete this submission? This action cannot be undone.</p>
|
|
<div class="alert alert-warning">
|
|
<strong>Submission ID:</strong> {{ submission.id }}<br>
|
|
<strong>Submitted:</strong> {{ submission.submitted_at|date:"M d, Y H:i" }}
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="button" class="btn btn-danger" id="confirmDeleteBtn">
|
|
<i class="fas fa-trash"></i> Delete Submission
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block extra_css %}
|
|
<style>
|
|
.response-value {
|
|
max-height: 200px;
|
|
overflow-y: auto;
|
|
}
|
|
.response-value ul {
|
|
margin: 0;
|
|
padding-left: 1.5rem;
|
|
}
|
|
.card {
|
|
border: 1px solid #e9ecef;
|
|
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
|
|
transition: box-shadow 0.15s ease-in-out;
|
|
}
|
|
.card:hover {
|
|
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
|
|
}
|
|
.card-title {
|
|
font-size: 0.9rem;
|
|
font-weight: 600;
|
|
color: #333;
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block extra_js %}
|
|
<script>
|
|
function exportSubmission() {
|
|
// Create export options modal or direct download
|
|
const format = prompt('Export format (csv, json, pdf):', 'csv');
|
|
if (format) {
|
|
window.open(`/recruitment/api/forms/{{ form.id }}/submissions/{{ submission.id }}/export/?format=${format}`, '_blank');
|
|
}
|
|
}
|
|
|
|
// Handle delete confirmation
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const deleteBtn = document.querySelector('a[href*="delete"]');
|
|
if (deleteBtn) {
|
|
deleteBtn.addEventListener('click', function(e) {
|
|
e.preventDefault();
|
|
const modal = new bootstrap.Modal(document.getElementById('deleteModal'));
|
|
modal.show();
|
|
});
|
|
}
|
|
|
|
// Handle confirm delete
|
|
const confirmDeleteBtn = document.getElementById('confirmDeleteBtn');
|
|
if (confirmDeleteBtn) {
|
|
confirmDeleteBtn.addEventListener('click', function() {
|
|
fetch(`/recruitment/api/forms/{{ form.id }}/submissions/{{ submission.id }}/delete/`, {
|
|
method: 'DELETE',
|
|
headers: {
|
|
'X-CSRFToken': getCsrfToken(),
|
|
'Content-Type': 'application/json'
|
|
}
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
alert('Submission deleted successfully!');
|
|
|
|
} else {
|
|
alert('Error deleting submission: ' + data.error);
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Error:', error);
|
|
alert('Error deleting submission');
|
|
});
|
|
});
|
|
}
|
|
});
|
|
|
|
function getCsrfToken() {
|
|
const cookie = document.cookie.split(';').find(c => c.trim().startsWith('csrftoken='));
|
|
return cookie ? cookie.split('=')[1] : '';
|
|
}
|
|
|
|
// Print functionality
|
|
function printSubmission() {
|
|
window.print();
|
|
}
|
|
|
|
// Add print button to header
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const headerActions = document.querySelector('.d-flex.justify-content-between.align-items-center.mb-4');
|
|
if (headerActions) {
|
|
const printBtn = document.createElement('button');
|
|
printBtn.className = 'btn btn-outline-secondary me-2';
|
|
printBtn.innerHTML = '<i class="fas fa-print"></i> Print';
|
|
printBtn.onclick = printSubmission;
|
|
headerActions.insertBefore(printBtn, headerActions.firstChild);
|
|
}
|
|
});
|
|
</script>
|
|
{% endblock %}
|