263 lines
14 KiB
HTML
263 lines
14 KiB
HTML
{% extends "portal_base.html" %}
|
|
{% load static %}
|
|
{% load i18n %}
|
|
{% block title %}{% trans "Document Management" %}{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="container mx-auto px-4 py-8">
|
|
<div class="flex justify-between items-center mb-6">
|
|
<h1 class="text-2xl font-bold text-gray-900">{% trans "Document Management" %}</h1>
|
|
<button onclick="showUploadModal()" class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg font-medium transition-colors">
|
|
<i class="fas fa-upload mr-2"></i>
|
|
{% trans "Upload Document" %}
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Documents Table -->
|
|
<div class="bg-white shadow rounded-lg overflow-hidden">
|
|
<div class="px-6 py-4">
|
|
<div class="overflow-x-auto">
|
|
<table class="min-w-full divide-y divide-gray-200">
|
|
<thead class="bg-gray-50">
|
|
<tr>
|
|
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
{% trans "Document Type" %}
|
|
</th>
|
|
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
{% trans "Description" %}
|
|
</th>
|
|
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
{% trans "File Name" %}
|
|
</th>
|
|
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
{% trans "Size" %}
|
|
</th>
|
|
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
{% trans "Uploaded" %}
|
|
</th>
|
|
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
{% trans "Actions" %}
|
|
</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="bg-white divide-y divide-gray-200">
|
|
{% for document in documents %}
|
|
<tr class="hover:bg-gray-50">
|
|
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
|
|
{{ document.get_document_type_display }}
|
|
</td>
|
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
|
{{ document.description|default:"-" }}
|
|
</td>
|
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
|
{% if document.file %}
|
|
<a href="{{ document.file.url }}" target="_blank" class="text-blue-600 hover:text-blue-800 underline">
|
|
{{ document.file.name|truncatechars:30 }}
|
|
</a>
|
|
{% else %}
|
|
-
|
|
{% endif %}
|
|
</td>
|
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
|
{% if document.file %}
|
|
{{ document.file.size|filesizeformat }}
|
|
{% else %}
|
|
-
|
|
{% endif %}
|
|
</td>
|
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
|
{{ document.created_at|date:"M d, Y" }}
|
|
</td>
|
|
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium">
|
|
<div class="flex space-x-2">
|
|
{% if document.file %}
|
|
<a href="{% url 'application_document_download' document.id %}"
|
|
class="text-green-600 hover:text-green-800 mr-3"
|
|
title="Download document">
|
|
<i class="fas fa-download"></i>
|
|
</a>
|
|
{% endif %}
|
|
<button onclick="deleteDocument({{ document.id }})"
|
|
class="text-red-600 hover:text-red-800"
|
|
title="Delete document">
|
|
<i class="fas fa-trash"></i>
|
|
</button>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
{% empty %}
|
|
<tr>
|
|
<td colspan="6" class="px-6 py-4 text-center text-gray-500">
|
|
{% trans "No documents uploaded yet." %}
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Upload Modal -->
|
|
<div id="uploadModal" class="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full z-50 hidden">
|
|
<div class="flex items-center justify-center min-h-screen px-4">
|
|
<div class="relative bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:max-w-lg sm:w-full">
|
|
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
|
|
<div class="flex justify-between items-start mb-4">
|
|
<h3 class="text-lg leading-6 font-medium text-gray-900">
|
|
{% trans "Upload New Document" %}
|
|
</h3>
|
|
<button onclick="hideUploadModal()" class="text-gray-400 hover:text-gray-600">
|
|
<i class="fas fa-times"></i>
|
|
</button>
|
|
</div>
|
|
<form id="uploadForm" enctype="multipart/form-data">
|
|
{% csrf_token %}
|
|
<div class="space-y-4">
|
|
<div>
|
|
<label for="document_type" class="block text-sm font-medium text-gray-700">{% trans "Document Type" %}</label>
|
|
<select id="document_type" name="document_type" required
|
|
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm">
|
|
<option value="resume">{% trans "Resume" %}</option>
|
|
<option value="cover_letter">{% trans "Cover Letter" %}</option>
|
|
<option value="transcript">{% trans "Transcript" %}</option>
|
|
<option value="certificate">{% trans "Certificate" %}</option>
|
|
<option value="id_document">{% trans "ID Document" %}</option>
|
|
<option value="portfolio">{% trans "Portfolio" %}</option>
|
|
<option value="other">{% trans "Other" %}</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label for="description" class="block text-sm font-medium text-gray-700">{% trans "Description" %}</label>
|
|
<textarea id="description" name="description" rows="3"
|
|
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
|
|
placeholder="Optional description of the document"></textarea>
|
|
</div>
|
|
<div>
|
|
<label for="file" class="block text-sm font-medium text-gray-700">{% trans "File" %}</label>
|
|
<input type="file" id="file" name="file" required
|
|
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
|
|
accept=".pdf,.doc,.docx,.txt,.jpg,.jpeg,.png">
|
|
</div>
|
|
</div>
|
|
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
|
|
<button type="button" onclick="hideUploadModal()"
|
|
class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:text-sm">
|
|
{% trans "Cancel" %}
|
|
</button>
|
|
<button type="submit"
|
|
class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm">
|
|
{% trans "Upload Document" %}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
function showUploadModal() {
|
|
document.getElementById('uploadModal').classList.remove('hidden');
|
|
}
|
|
|
|
function hideUploadModal() {
|
|
document.getElementById('uploadModal').classList.add('hidden');
|
|
}
|
|
|
|
function deleteDocument(documentId) {
|
|
if (confirm('{% trans "Are you sure you want to delete this document?" %}')) {
|
|
fetch(`/candidate/documents/${documentId}/delete/`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'X-CSRFToken': getCookie('csrftoken'),
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({})
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
// Remove the row from table
|
|
const row = event.target.closest('tr');
|
|
row.remove();
|
|
|
|
// Show success message
|
|
showNotification('{% trans "Document deleted successfully!" %}', 'success');
|
|
} else {
|
|
showNotification(data.error || '{% trans "Failed to delete document" %}', 'error');
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Error:', error);
|
|
showNotification('{% trans "An error occurred while deleting the document" %}', 'error');
|
|
});
|
|
}
|
|
}
|
|
|
|
// Handle form submission
|
|
document.getElementById('uploadForm').addEventListener('submit', function(e) {
|
|
e.preventDefault();
|
|
|
|
const formData = new FormData(this);
|
|
const applicationId = '{{ application.id }}';
|
|
|
|
fetch(`/candidate/documents/upload/{{ application.slug }}/`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'X-CSRFToken': getCookie('csrftoken'),
|
|
},
|
|
body: formData
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
hideUploadModal();
|
|
// Reload the page to show the new document
|
|
window.location.reload();
|
|
} else {
|
|
showNotification(data.error || '{% trans "Failed to upload document" %}', 'error');
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Error:', error);
|
|
showNotification('{% trans "An error occurred while uploading the document" %}', 'error');
|
|
});
|
|
});
|
|
|
|
function getCookie(name) {
|
|
const value = `; ${document.cookie}`.match(`;\\s*${name}=([^;]*)`);
|
|
return value ? value[1] : null;
|
|
}
|
|
|
|
function showNotification(message, type) {
|
|
// Create notification element
|
|
const notification = document.createElement('div');
|
|
notification.className = `fixed top-4 right-4 p-4 rounded-lg shadow-lg z-50 ${
|
|
type === 'success' ? 'bg-green-100 border-green-400 text-green-700' : 'bg-red-100 border-red-400 text-red-700'
|
|
}`;
|
|
notification.innerHTML = `
|
|
<div class="flex">
|
|
<div class="flex-shrink-0">
|
|
${type === 'success' ?
|
|
'<svg class="h-5 w-5 text-green-400" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M10 18a8 8 0 00-8 8 0 018 0zm-3.707-9.293a1 1 0 00-1.414 1.414L10 10.586 3.414a1 1 0 01.414-1.414l-4.293-4.293a1 1 0 00-1.414 1.414L10 14.172a1 1 0 01.414-1.414l4.293-4.293a1 1 0 00-1.414 1.414L10 5.828a1 1 0 01.414-1.414z" clip-rule="evenodd"></path></svg>' :
|
|
'<svg class="h-5 w-5 text-red-400" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M10 18a8 8 0 00-8 8 0 018 0zm-3.707-9.293a1 1 0 00-1.414 1.414L10 10.586 3.414a1 1 0 01.414-1.414l-4.293-4.293a1 1 0 00-1.414 1.414L10 14.172a1 1 0 01.414-1.414l4.293-4.293a1 1 0 00-1.414 1.414z" clip-rule="evenodd"></path></svg>'
|
|
}
|
|
</div>
|
|
<div class="ml-3">
|
|
<p class="text-sm font-medium">${message}</p>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
document.body.appendChild(notification);
|
|
|
|
// Remove after 3 seconds
|
|
setTimeout(() => {
|
|
notification.remove();
|
|
}, 3000);
|
|
}
|
|
</script>
|
|
{% endblock %}
|