175 lines
8.7 KiB
HTML
175 lines
8.7 KiB
HTML
{% extends "base.html" %}
|
|
{% load i18n %}
|
|
|
|
{% block title %}{% trans "Integration Settings" %}{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="container-fluid py-4">
|
|
<nav aria-label="breadcrumb" class="mb-3">
|
|
<ol class="breadcrumb">
|
|
<li class="breadcrumb-item"><a href="{% url 'settings' %}" class="text-decoration-none text-muted">{% trans "Settings" %}</a></li>
|
|
<li class="breadcrumb-item active" style="color: #F43B5E; font-weight: 600;">{% trans "Integrations" %}</li>
|
|
</ol>
|
|
</nav>
|
|
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<div>
|
|
<h1 class="h3 mb-1 text-dark fw-bold">
|
|
<i class="fas fa-sliders-h me-2 text-primary-theme"></i>
|
|
{% trans "Integration Settings" %}
|
|
</h1>
|
|
<p class="text-muted small mb-0">{% trans "Manage API keys, Webhooks, and external service configurations." %}</p>
|
|
</div>
|
|
<a href="{% url 'settings_create' %}" class="btn btn-main-action shadow-sm">
|
|
<i class="fas fa-plus-circle me-2"></i>{% trans "Add New Setting" %}
|
|
</a>
|
|
</div>
|
|
|
|
<div class="card border-0 shadow-sm mb-4">
|
|
<div class="card-body p-3">
|
|
<form method="get" class="row g-2">
|
|
<div class="col-md-9">
|
|
<div class="input-group">
|
|
<span class="input-group-text bg-white border-end-0"><i class="fas fa-search text-muted"></i></span>
|
|
<input type="text" class="form-control border-start-0 ps-0" name="q"
|
|
placeholder="{% trans 'Search by name, key, or category...' %}" value="{{ search_query }}">
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3 d-grid gap-2 d-md-flex">
|
|
<button type="submit" class="btn btn-main-action px-4">{% trans "Search" %}</button>
|
|
{% if search_query %}
|
|
<a href="{% url 'settings_list' %}" class="btn btn-light border">{% trans "Clear" %}</a>
|
|
{% endif %}
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
{% if page_obj %}
|
|
<div class="card border-0 shadow-sm">
|
|
<div class="table-responsive">
|
|
<table class="table table-hover align-middle mb-0">
|
|
<thead class="bg-light">
|
|
<tr>
|
|
<th class="ps-4" style="width: 40%;">{% trans "Setting Name & Key" %}</th>
|
|
<th style="width: 40%;">{% trans "Value" %}</th>
|
|
<th class="text-end pe-4">{% trans "Actions" %}</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for setting in page_obj %}
|
|
<tr>
|
|
<td class="ps-4">
|
|
<div class="fw-bold text-dark">{{ setting.name|default:setting.key }}</div>
|
|
<code class="small text-muted" style="font-size: 0.75rem;">{{ setting.key }}</code>
|
|
</td>
|
|
|
|
<td>
|
|
<div class="d-flex align-items-center">
|
|
<div class="text-truncate" style="max-width: 300px; min-width: 150px;">
|
|
<span class="font-monospace small text-muted reveal-toggle"
|
|
data-full-value="{{ setting.value }}"
|
|
data-masked="••••••••••••••••"
|
|
onclick="toggleSecret(this)"
|
|
style="cursor: pointer; user-select: none;">
|
|
••••••••••••••••
|
|
</span>
|
|
</div>
|
|
<div class="ms-2 d-flex">
|
|
<button type="button" class="btn btn-link btn-sm p-1 text-decoration-none text-secondary"
|
|
onclick="toggleSecret(this.parentElement.previousElementSibling.firstElementChild)"
|
|
title="{% trans 'Show/Hide' %}">
|
|
<i class="fas fa-eye fa-xs"></i>
|
|
</button>
|
|
<button type="button" class="btn btn-link btn-sm p-1 text-decoration-none text-secondary"
|
|
onclick="copyToClipboard('{{ setting.value|escapejs }}', this)"
|
|
title="{% trans 'Copy to Clipboard' %}">
|
|
<i class="fas fa-copy fa-xs"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td class="text-end pe-4">
|
|
<div class="btn-group shadow-sm">
|
|
<a href="{% url 'settings_detail' setting.pk %}" class="btn btn-white btn-sm border" title="{% trans 'View' %}">
|
|
<i class="fas fa-eye text-primary"></i>
|
|
</a>
|
|
<a href="{% url 'settings_update' setting.pk %}" class="btn btn-white btn-sm border" title="{% trans 'Edit' %}">
|
|
<i class="fas fa-edit text-secondary"></i>
|
|
</a>
|
|
{% comment %} <button type="button" class="btn btn-white btn-sm border"
|
|
onclick="deleteDocument(this, {{ setting.pk }})" title="{% trans 'Delete' %}">
|
|
<i class="fas fa-trash-alt text-danger"></i>
|
|
</button> {% endcomment %}
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
{% if page_obj.has_other_pages %}
|
|
<div class="card-footer bg-white border-top-0 py-3">
|
|
{% include "includes/paginator.html" %}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
{% else %}
|
|
<div class="card border-0 shadow-sm py-5">
|
|
<div class="card-body text-center">
|
|
<div class="mb-4">
|
|
<i class="fas fa-tools fa-4x text-light"></i>
|
|
</div>
|
|
<h4 class="text-dark">{% trans "No settings found" %}</h4>
|
|
<p class="text-muted mx-auto" style="max-width: 400px;">
|
|
{% if search_query %}
|
|
{% blocktrans %}We couldn't find any settings matching "{{ search_query }}". Please try a different term.{% endblocktrans %}
|
|
{% else %}
|
|
{% trans "You haven't added any integration settings yet. Get started by adding your first API key or configuration." %}
|
|
{% endif %}
|
|
</p>
|
|
<a href="{% url 'settings_create' %}" class="btn btn-main-action mt-2">
|
|
<i class="fas fa-plus me-2"></i>{% trans "Create First Setting" %}
|
|
</a>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<style>
|
|
|
|
.reveal-toggle { transition: color 0.2s; }
|
|
.reveal-toggle:hover { color: #212529 !important; }
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block customJS %}
|
|
<script>
|
|
function toggleSecret(element) {
|
|
const fullValue = element.getAttribute('data-full-value');
|
|
const maskedValue = element.getAttribute('data-masked');
|
|
// Find the icon inside the button next to the div container
|
|
const icon = element.parentElement.parentElement.querySelector('.fa-eye, .fa-eye-slash');
|
|
|
|
if (element.textContent.trim() === maskedValue) {
|
|
element.textContent = fullValue;
|
|
element.classList.replace('text-muted', 'text-dark');
|
|
if(icon) icon.classList.replace('fa-eye', 'fa-eye-slash');
|
|
} else {
|
|
element.textContent = maskedValue;
|
|
element.classList.replace('text-dark', 'text-muted');
|
|
if(icon) icon.classList.replace('fa-eye-slash', 'fa-eye');
|
|
}
|
|
}
|
|
|
|
function copyToClipboard(text, btn) {
|
|
navigator.clipboard.writeText(text).then(() => {
|
|
const icon = btn.querySelector('i');
|
|
const originalClass = icon.className;
|
|
icon.className = 'fas fa-check text-success fa-xs';
|
|
setTimeout(() => { icon.className = originalClass; }, 2000);
|
|
});
|
|
}
|
|
</script>
|
|
{% endblock %} |