236 lines
12 KiB
HTML
236 lines
12 KiB
HTML
{% extends "base.html" %}
|
|
{% load static i18n %}
|
|
{% load widget_tweaks %}
|
|
|
|
{% block title %}{{ title }}{% 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">
|
|
<h1 class="h3 mb-0">{{ title }}</h1>
|
|
<a href="{% url 'source_list' %}" class="btn btn-outline-secondary">
|
|
<i class="fas fa-arrow-left"></i> Back to Sources
|
|
</a>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<form method="post" novalidate>
|
|
{% csrf_token %}
|
|
|
|
{% if form.non_field_errors %}
|
|
<div class="alert alert-danger">
|
|
{% for error in form.non_field_errors %}
|
|
{{ error }}
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label for="{{ form.name.id_for_label }}" class="form-label">
|
|
{{ form.name.label }} <span class="text-danger">*</span>
|
|
</label>
|
|
{{ form.name|add_class:"form-control" }}
|
|
{% if form.name.errors %}
|
|
<div class="invalid-feedback d-block">
|
|
{% for error in form.name.errors %}
|
|
{{ error }}
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
<div class="form-text">{{ form.name.help_text }}</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label for="{{ form.source_type.id_for_label }}" class="form-label">
|
|
{{ form.source_type.label }} <span class="text-danger">*</span>
|
|
</label>
|
|
{{ form.source_type }}
|
|
{% if form.source_type.errors %}
|
|
<div class="invalid-feedback d-block">
|
|
{% for error in form.source_type.errors %}
|
|
{{ error }}
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
<div class="form-text">{{ form.source_type.help_text }}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label for="{{ form.ip_address.id_for_label }}" class="form-label">
|
|
{{ form.ip_address.label }} <span class="text-danger">*</span>
|
|
</label>
|
|
{{ form.ip_address|add_class:"form-control" }}
|
|
{% if form.ip_address.errors %}
|
|
<div class="invalid-feedback d-block">
|
|
{% for error in form.ip_address.errors %}
|
|
{{ error }}
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
<div class="form-text">{{ form.ip_address.help_text }}</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label for="{{ form.ip_address.id_for_label }}" class="form-label">
|
|
{{ form.trusted_ips.label }} <span class="text-danger">*</span>
|
|
</label>
|
|
{{ form.trusted_ips|add_class:"form-control" }}
|
|
{% if form.trusted_ips.errors %}
|
|
<div class="invalid-feedback d-block">
|
|
{% for error in form.trusted_ips.errors %}
|
|
{{ error }}
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
<div class="form-text">{{ form.trusted_ips.help_text }}</div>
|
|
</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="{{ form.description.id_for_label }}" class="form-label">
|
|
{{ form.description.label }}
|
|
</label>
|
|
{{ form.description|add_class:"form-control" }}
|
|
{% if form.description.errors %}
|
|
<div class="invalid-feedback d-block">
|
|
{% for error in form.description.errors %}
|
|
{{ error }}
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
<div class="form-text">{{ form.description.help_text }}</div>
|
|
</div>
|
|
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<div class="form-check">
|
|
{{ form.is_active|add_class:"form-check-input" }}
|
|
<label for="{{ form.is_active.id_for_label }}" class="form-check-label">
|
|
{{ form.is_active.label }}
|
|
</label>
|
|
</div>
|
|
{% if form.is_active.errors %}
|
|
<div class="invalid-feedback d-block">
|
|
{% for error in form.is_active.errors %}
|
|
{{ error }}
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
<div class="form-text">{{ form.is_active.help_text }}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<!-- API Credentials Section -->
|
|
{% if source %}
|
|
<div class="card bg-light mb-4">
|
|
<div class="card-header">
|
|
<h6 class="mb-0">API Credentials</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label class="form-label">API Key</label>
|
|
<div class="input-group">
|
|
<input type="text" class="form-control" value="{{ source.api_key }}" readonly>
|
|
<button type="button" class="btn btn-outline-secondary"
|
|
hx-post="{% url 'copy_to_clipboard' %}"
|
|
hx-vals='{"text": "{{ source.api_key }}"}'
|
|
title="Copy to clipboard">
|
|
<i class="fas fa-copy"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label class="form-label">API Secret</label>
|
|
<div class="input-group">
|
|
<input type="password" class="form-control" value="{{ source.api_secret }}" readonly id="api-secret">
|
|
<button type="button" class="btn btn-outline-secondary" onclick="toggleSecretVisibility()">
|
|
<i class="fas fa-eye" id="secret-toggle-icon"></i>
|
|
</button>
|
|
<button type="button" class="btn btn-outline-secondary"
|
|
hx-post="{% url 'copy_to_clipboard' %}"
|
|
hx-vals='{"text": "{{ source.api_secret }}"}'
|
|
title="Copy to clipboard">
|
|
<i class="fas fa-copy"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="text-end">
|
|
<a href="{% url 'generate_api_keys' source.pk %}" class="btn btn-warning">
|
|
<i class="fas fa-key"></i> Generate New Keys
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<div class="d-flex justify-content-between">
|
|
<a href="{% url 'source_list' %}" class="btn btn-outline-secondary">
|
|
<i class="fas fa-times"></i> Cancel
|
|
</a>
|
|
<button type="submit" class="btn btn-main-action">
|
|
<i class="fas fa-save me-1"></i> {% trans "Save" %}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block extra_js %}
|
|
<script>
|
|
function toggleSecretVisibility() {
|
|
const secretInput = document.getElementById('api-secret');
|
|
const toggleIcon = document.getElementById('secret-toggle-icon');
|
|
|
|
if (secretInput.type === 'password') {
|
|
secretInput.type = 'text';
|
|
toggleIcon.classList.remove('fa-eye');
|
|
toggleIcon.classList.add('fa-eye-slash');
|
|
} else {
|
|
secretInput.type = 'password';
|
|
toggleIcon.classList.remove('fa-eye-slash');
|
|
toggleIcon.classList.add('fa-eye');
|
|
}
|
|
}
|
|
|
|
// Handle HTMX copy to clipboard feedback
|
|
document.body.addEventListener('htmx:afterRequest', function(evt) {
|
|
if (evt.detail.successful && evt.detail.target.matches('[hx-post*="copy_to_clipboard"]')) {
|
|
const button = evt.detail.target;
|
|
const originalIcon = button.innerHTML;
|
|
button.innerHTML = '<i class="fas fa-check"></i>';
|
|
button.classList.remove('btn-outline-secondary');
|
|
button.classList.add('btn-success');
|
|
|
|
setTimeout(() => {
|
|
button.innerHTML = originalIcon;
|
|
button.classList.remove('btn-success');
|
|
button.classList.add('btn-outline-secondary');
|
|
}, 2000);
|
|
}
|
|
});
|
|
</script>
|
|
{% endblock %}
|