377 lines
18 KiB
HTML
377 lines
18 KiB
HTML
{% extends 'base.html' %}
|
|
{% load i18n %}
|
|
|
|
{% block title %}
|
|
{% if title %}{{ title }} | {% endif %}{% trans "Source" %} | {% trans "Recruitment System" %}
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
|
|
<!-- Script to define functions globally before buttons are rendered -->
|
|
<script>
|
|
function copyToClipboard(elementId) {
|
|
const element = document.getElementById(elementId);
|
|
if (element) {
|
|
const text = element.value;
|
|
navigator.clipboard.writeText(text).then(function() {
|
|
const button = event.target.closest('button');
|
|
if (button) {
|
|
const orig = button.innerHTML;
|
|
button.innerHTML = '<i class="fas fa-check"></i>';
|
|
button.classList.add('btn-success');
|
|
button.classList.remove('btn-outline-secondary');
|
|
setTimeout(function() {
|
|
button.innerHTML = orig;
|
|
button.classList.remove('btn-success');
|
|
button.classList.add('btn-outline-secondary');
|
|
}, 2000);
|
|
}
|
|
}).catch(function(err) {
|
|
console.error('Failed to copy text: ', err);
|
|
alert('{% trans "Failed to copy to clipboard" %}');
|
|
});
|
|
} else {
|
|
console.error('Element not found:', elementId);
|
|
}
|
|
}
|
|
|
|
function generateRandomKey(elementId, length) {
|
|
const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~';
|
|
let result = '';
|
|
for (let i = 0; i < length; i++) {
|
|
result += alphabet.charAt(Math.floor(Math.random() * alphabet.length));
|
|
}
|
|
|
|
const element = document.getElementById(elementId);
|
|
if (element) {
|
|
element.value = result;
|
|
|
|
const button = event.target.closest('button');
|
|
if (button) {
|
|
const orig = button.innerHTML;
|
|
button.innerHTML = '<i class="fas fa-check"></i>';
|
|
button.classList.add('btn-success');
|
|
button.classList.remove('btn-outline-secondary');
|
|
setTimeout(function() {
|
|
button.innerHTML = orig;
|
|
button.classList.remove('btn-success');
|
|
button.classList.add('btn-outline-secondary');
|
|
}, 1500);
|
|
}
|
|
} else {
|
|
console.error('Element not found:', elementId);
|
|
}
|
|
}
|
|
|
|
// Make functions globally available
|
|
window.copyToClipboard = copyToClipboard;
|
|
window.generateRandomKey = generateRandomKey;
|
|
</script>
|
|
|
|
<div class="container-fluid py-4">
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="card shadow">
|
|
<div class="card-header py-3">
|
|
<h6 class="m-0 font-weight-bold text-primary">
|
|
{% if title %}{{ title }}{% else %}{% trans "Create New Source" %}{% endif %}
|
|
</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<form method="post" id="sourceForm">
|
|
{% csrf_token %}
|
|
|
|
<!-- Form Messages -->
|
|
{% if form.errors %}
|
|
<div class="alert alert-danger">
|
|
<h5 class="alert-heading">{% trans "Please correct the errors below:" %}</h5>
|
|
{% for field in form %}
|
|
{% if field.errors %}
|
|
<p class="mb-0">{{ field.label }}: {{ field.errors|join:", " }}</p>
|
|
{% endif %}
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if messages %}
|
|
{% for message in messages %}
|
|
<div class="alert alert-{{ message.tags }} alert-dismissible fade show" role="alert">
|
|
{{ message }}
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
|
</div>
|
|
{% endfor %}
|
|
{% endif %}
|
|
|
|
<!-- Basic Information -->
|
|
<div class="row mb-4">
|
|
<div class="col-12">
|
|
<h5 class="mb-3">{% trans "Basic Information" %}</h5>
|
|
</div>
|
|
|
|
<div class="col-md-6 mb-3">
|
|
{{ form.name.label_tag }}
|
|
{{ form.name }}
|
|
{% if form.name.help_text %}
|
|
<small class="form-text text-muted">{{ form.name.help_text }}</small>
|
|
{% endif %}
|
|
{% if form.name.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.name.errors }}</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div class="col-md-6 mb-3">
|
|
{{ form.source_type.label_tag }}
|
|
{{ form.source_type }}
|
|
{% if form.source_type.help_text %}
|
|
<small class="form-text text-muted">{{ form.source_type.help_text }}</small>
|
|
{% endif %}
|
|
{% if form.source_type.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.source_type.errors }}</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div class="col-12 mb-3">
|
|
{{ form.description.label_tag }}
|
|
{{ form.description }}
|
|
{% if form.description.help_text %}
|
|
<small class="form-text text-muted">{{ form.description.help_text }}</small>
|
|
{% endif %}
|
|
{% if form.description.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.description.errors }}</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Network Configuration -->
|
|
<div class="row mb-4">
|
|
<div class="col-12">
|
|
<h5 class="mb-3">{% trans "Network Configuration" %}</h5>
|
|
</div>
|
|
|
|
<div class="col-md-6 mb-3">
|
|
{{ form.ip_address.label_tag }}
|
|
{{ form.ip_address }}
|
|
{% if form.ip_address.help_text %}
|
|
<small class="form-text text-muted">{{ form.ip_address.help_text }}</small>
|
|
{% endif %}
|
|
{% if form.ip_address.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.ip_address.errors }}</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div class="col-md-6 mb-3">
|
|
{{ form.trusted_ips.label_tag }}
|
|
{{ form.trusted_ips }}
|
|
{% if form.trusted_ips.help_text %}
|
|
<small class="form-text text-muted">{{ form.trusted_ips.help_text }}</small>
|
|
{% endif %}
|
|
{% if form.trusted_ips.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.trusted_ips.errors }}</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Settings -->
|
|
<div class="row mb-4">
|
|
<div class="col-12">
|
|
<h5 class="mb-3">{% trans "Settings" %}</h5>
|
|
</div>
|
|
|
|
<div class="col-md-6 mb-3">
|
|
{{ form.integration_version.label_tag }}
|
|
{{ form.integration_version }}
|
|
{% if form.integration_version.help_text %}
|
|
<small class="form-text text-muted">{{ form.integration_version.help_text }}</small>
|
|
{% endif %}
|
|
{% if form.integration_version.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.integration_version.errors }}</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div class="col-md-6 mb-3">
|
|
<div class="form-check form-switch">
|
|
{{ form.is_active }}
|
|
{{ form.is_active.label_tag }}
|
|
</div>
|
|
{% if form.is_active.help_text %}
|
|
<small class="form-text text-muted">{{ form.is_active.help_text }}</small>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- API Configuration -->
|
|
<div class="row mb-4">
|
|
<div class="col-12">
|
|
<h5 class="mb-3">{% trans "API Configuration" %}</h5>
|
|
</div>
|
|
|
|
<div class="col-12 mb-3">
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<div class="row align-items-center">
|
|
<div class="col-md-8">
|
|
<h6 class="mb-0">{% trans "API Keys" %}</h6>
|
|
<small class="text-muted">{% trans "Generate secure API keys for external integrations" %}</small>
|
|
</div>
|
|
<div class="col-md-4 text-end">
|
|
<div class="form-check form-switch">
|
|
{{ form.generate_keys }}
|
|
{{ form.generate_keys.label_tag }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Generated API Key -->
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label">{% trans "API Key" %}</label>
|
|
<div class="input-group">
|
|
{{ form.api_key_generated }}
|
|
<button class="btn btn-outline-secondary" type="button"
|
|
id="generateApiKey"
|
|
onclick="generateRandomKey('id_api_key_generated', 32)"
|
|
title="{% trans 'Generate random API key' %}">
|
|
<i class="fas fa-sync-alt"></i>
|
|
</button>
|
|
<button class="btn btn-outline-secondary" type="button"
|
|
id="copyApiKey"
|
|
onclick="copyToClipboard('id_api_key_generated')"
|
|
title="{% trans 'Copy to clipboard' %}">
|
|
<i class="fas fa-copy"></i>
|
|
</button>
|
|
</div>
|
|
{% if form.api_key_generated.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.api_key_generated.errors }}</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Generated API Secret -->
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label">{% trans "API Secret" %}</label>
|
|
<div class="input-group">
|
|
{{ form.api_secret_generated }}
|
|
<button class="btn btn-outline-secondary" type="button"
|
|
id="generateApiSecret"
|
|
onclick="generateRandomKey('id_api_secret_generated', 64)"
|
|
title="{% trans 'Generate random API secret' %}">
|
|
<i class="fas fa-sync-alt"></i>
|
|
</button>
|
|
<button class="btn btn-outline-secondary" type="button"
|
|
id="copyApiSecret"
|
|
onclick="copyToClipboard('id_api_secret_generated')"
|
|
title="{% trans 'Copy to clipboard' %}">
|
|
<i class="fas fa-copy"></i>
|
|
</button>
|
|
</div>
|
|
{% if form.api_secret_generated.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.api_secret_generated.errors }}</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Form Actions -->
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<hr>
|
|
<div class="d-flex justify-content-between">
|
|
<a href="{% url 'source_list' %}" class="btn btn-secondary">
|
|
<i class="fas fa-times me-2"></i>
|
|
{% trans "Cancel" %}
|
|
</a>
|
|
<div>
|
|
<button type="submit" class="btn btn-primary">
|
|
<i class="fas fa-save me-2"></i>
|
|
{% trans "Save Source" %}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block customJS %}
|
|
<script>
|
|
// Function to copy text to clipboard
|
|
function copyToClipboard(elementId) {
|
|
const element = document.getElementById(elementId);
|
|
if (element) {
|
|
const text = element.value;
|
|
navigator.clipboard.writeText(text).then(function() {
|
|
// Show success message
|
|
const button = event.target.closest('button');
|
|
if (button) {
|
|
const originalContent = button.innerHTML;
|
|
button.innerHTML = '<i class="fas fa-check"></i>';
|
|
button.classList.add('btn-success');
|
|
button.classList.remove('btn-outline-secondary');
|
|
|
|
setTimeout(function() {
|
|
button.innerHTML = originalContent;
|
|
button.classList.remove('btn-success');
|
|
button.classList.add('btn-outline-secondary');
|
|
}, 2000);
|
|
}
|
|
}).catch(function(err) {
|
|
console.error('Failed to copy text: ', err);
|
|
alert('{% trans "Failed to copy to clipboard" %}');
|
|
});
|
|
} else {
|
|
console.error('Element not found:', elementId);
|
|
}
|
|
}
|
|
|
|
// Function to generate random key
|
|
function generateRandomKey(elementId, length) {
|
|
const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~';
|
|
let result = '';
|
|
for (let i = 0; i < length; i++) {
|
|
result += alphabet.charAt(Math.floor(Math.random() * alphabet.length));
|
|
}
|
|
console.log(elementId);
|
|
const element = document.getElementById(elementId);
|
|
if (element) {
|
|
element.value = result;
|
|
|
|
// Show success animation on the generate button
|
|
const button = event.target.closest('button');
|
|
if (button) {
|
|
const originalContent = button.innerHTML;
|
|
button.innerHTML = '<i class="fas fa-check"></i>';
|
|
button.classList.add('btn-success');
|
|
button.classList.remove('btn-outline-secondary');
|
|
|
|
setTimeout(function() {
|
|
button.innerHTML = originalContent;
|
|
button.classList.remove('btn-success');
|
|
button.classList.add('btn-outline-secondary');
|
|
}, 1500);
|
|
}
|
|
} else {
|
|
console.error('Element not found:', elementId);
|
|
}
|
|
}
|
|
|
|
// Initialize after DOM is fully loaded
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const generateKeysCheckbox = document.getElementById('id_generate_keys');
|
|
if (generateKeysCheckbox) {
|
|
// If API keys are already generated, show them
|
|
const apiKeyField = document.getElementById('id_api_key_generated');
|
|
const apiSecretField = document.getElementById('id_api_secret_generated');
|
|
|
|
if (apiKeyField && apiSecretField && (apiKeyField.value || apiSecretField.value)) {
|
|
generateKeysCheckbox.checked = true;
|
|
}
|
|
}
|
|
});
|
|
</script>
|
|
{% endblock %}
|