ATS/templates/recruitment/settings_list.html
2026-01-29 14:19:03 +03:00

330 lines
15 KiB
HTML

{% extends "base.html" %}
{% load i18n %}
{% block title %}{% trans "Integration Settings" %}{% endblock %}
{% block customCSS %}
<style>
/* Card Hover Effects */
.settings-card {
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.settings-card:hover {
transform: translateY(-2px);
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1);
}
/* Table Row Hover */
.table-row {
transition: all 0.2s ease;
}
.table-row:hover {
background-color: rgba(157, 34, 53, 0.02);
transform: scale(1.01);
}
/* Button Hover Effects */
.btn-action {
transition: all 0.2s ease;
}
.btn-action:hover {
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.btn-primary {
transition: all 0.2s ease;
}
.btn-primary:hover {
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(157, 34, 53, 0.4);
}
.btn-secondary {
transition: all 0.2s ease;
}
.btn-secondary:hover {
background-color: rgba(157, 34, 53, 0.05);
border-color: rgba(157, 34, 53, 0.3);
}
/* Input Focus Animation */
.search-input {
transition: all 0.2s ease;
}
.search-input:focus {
transform: translateY(-1px);
}
/* Secret Value Toggle */
.secret-value {
transition: all 0.2s ease;
cursor: pointer;
user-select: none;
}
.secret-value:hover {
color: #9d2235 !important;
}
/* Empty State Animation */
.empty-icon {
animation: float 3s ease-in-out infinite;
}
@keyframes float {
0%, 100% {
transform: translateY(0px);
}
50% {
transform: translateY(-10px);
}
}
</style>
{% endblock %}
{% block content %}
<div class="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
<!-- Breadcrumb -->
<nav aria-label="breadcrumb" class="mb-6">
<ol class="flex items-center gap-2 text-sm">
<li>
<a href="{% url 'settings' %}"
class="text-gray-500 hover:text-temple-red transition-colors flex items-center gap-1">
<i data-lucide="settings" class="w-4 h-4"></i>
{% trans "Settings" %}
</a>
</li>
<li class="text-gray-400">/</li>
<li class="text-temple-red font-semibold flex items-center gap-1">
<i data-lucide="link-2" class="w-4 h-4"></i>
{% trans "Integrations" %}
</li>
</ol>
</nav>
<!-- Page Header -->
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 mb-6">
<div>
<h1 class="text-2xl sm:text-3xl font-bold text-gray-900 mb-2 flex items-center gap-3">
<div class="w-12 h-12 rounded-xl bg-temple-red/10 flex items-center justify-center">
<i data-lucide="sliders-horizontal" class="w-6 h-6 text-temple-red"></i>
</div>
{% trans "Integration Settings" %}
</h1>
<p class="text-gray-500 text-sm sm:text-base">
{% trans "Manage API keys, Webhooks, and external service configurations." %}
</p>
</div>
<a href="{% url 'settings_create' %}"
class="btn-primary inline-flex items-center gap-2 px-6 py-3 bg-temple-red text-white rounded-xl font-semibold shadow-lg hover:shadow-xl">
<i data-lucide="plus-circle" class="w-5 h-5"></i>
{% trans "Add New Setting" %}
</a>
</div>
<!-- Search Card -->
<div class="settings-card bg-white rounded-2xl shadow-sm border border-gray-200 mb-6">
<div class="p-4 sm:p-6">
<form method="get" class="flex flex-col sm:flex-row gap-3">
<div class="flex-1 relative">
<span class="absolute left-4 top-1/2 -translate-y-1/2 text-gray-400">
<i data-lucide="search" class="w-5 h-5"></i>
</span>
<input type="text"
class="search-input w-full pl-12 pr-4 py-3 rounded-xl border-2 border-gray-200 bg-white focus:outline-none focus:border-temple-red focus:ring-4 focus:ring-temple-red/10 text-gray-900 placeholder-gray-400"
name="q"
placeholder="{% trans 'Search by name, key, or category...' %}"
value="{{ search_query }}">
</div>
<div class="flex gap-3">
<button type="submit"
class="btn-action inline-flex items-center gap-2 px-6 py-3 bg-temple-red text-white rounded-xl font-semibold shadow-md hover:shadow-lg">
<i data-lucide="search" class="w-5 h-5"></i>
{% trans "Search" %}
</button>
{% if search_query %}
<a href="{% url 'settings_list' %}"
class="btn-secondary inline-flex items-center gap-2 px-6 py-3 border-2 border-gray-200 text-gray-700 rounded-xl font-semibold hover:border-gray-300">
<i data-lucide="x-circle" class="w-5 h-5"></i>
{% trans "Clear" %}
</a>
{% endif %}
</div>
</form>
</div>
</div>
{% if page_obj %}
<!-- Settings Table Card -->
<div class="settings-card bg-white rounded-2xl shadow-sm border border-gray-200 overflow-hidden">
<div class="overflow-x-auto">
<table class="w-full">
<thead class="bg-gradient-to-r from-gray-50 to-white">
<tr>
<th class="text-left px-6 py-4 text-sm font-semibold text-gray-700 border-b border-gray-200">
<div class="flex items-center gap-2">
<i data-lucide="database" class="w-4 h-4 text-gray-400"></i>
{% trans "Setting Name & Key" %}
</div>
</th>
<th class="text-left px-6 py-4 text-sm font-semibold text-gray-700 border-b border-gray-200">
<div class="flex items-center gap-2">
<i data-lucide="key" class="w-4 h-4 text-gray-400"></i>
{% trans "Value" %}
</div>
</th>
<th class="text-right px-6 py-4 text-sm font-semibold text-gray-700 border-b border-gray-200">
<div class="flex items-center gap-2 justify-end">
{% trans "Actions" %}
<i data-lucide="more-horizontal" class="w-4 h-4 text-gray-400"></i>
</div>
</th>
</tr>
</thead>
<tbody>
{% for setting in page_obj %}
<tr class="table-row border-b border-gray-100 last:border-b-0">
<td class="px-6 py-4">
<div class="flex flex-col gap-1">
<div class="font-semibold text-gray-900">{{ setting.name|default:setting.key }}</div>
<code class="text-xs text-gray-500 bg-gray-100 px-2 py-1 rounded-lg font-mono">
{{ setting.key }}
</code>
</div>
</td>
<td class="px-6 py-4">
<div class="flex items-center gap-3">
<div class="flex-1 min-w-0">
<span class="secret-value font-mono text-sm text-gray-600 bg-gray-50 px-3 py-2 rounded-lg inline-block"
data-full-value="{{ setting.value }}"
data-masked="••••••••••••••"
onclick="toggleSecret(this)">
•••••••••••••
</span>
</div>
<div class="flex items-center gap-1">
<button type="button"
class="btn-action w-9 h-9 rounded-lg bg-gray-100 hover:bg-gray-200 flex items-center justify-center text-gray-500 hover:text-temple-red transition-colors"
onclick="toggleSecret(this.parentElement.previousElementSibling.firstElementChild)"
title="{% trans 'Show/Hide' %}">
<i data-lucide="eye" class="w-4 h-4"></i>
</button>
<button type="button"
class="btn-action w-9 h-9 rounded-lg bg-gray-100 hover:bg-gray-200 flex items-center justify-center text-gray-500 hover:text-temple-red transition-colors"
onclick="copyToClipboard('{{ setting.value|escapejs }}', this)"
title="{% trans 'Copy to Clipboard' %}">
<i data-lucide="copy" class="w-4 h-4"></i>
</button>
</div>
</div>
</td>
<td class="px-6 py-4 text-right">
<div class="flex items-center justify-end gap-2">
<a href="{% url 'settings_detail' setting.pk %}"
class="btn-action w-9 h-9 rounded-lg bg-temple-red/10 hover:bg-temple-red/20 flex items-center justify-center text-temple-red transition-colors"
title="{% trans 'View' %}">
<i data-lucide="eye" class="w-4 h-4"></i>
</a>
<a href="{% url 'settings_update' setting.pk %}"
class="btn-action w-9 h-9 rounded-lg bg-gray-100 hover:bg-gray-200 flex items-center justify-center text-gray-500 hover:text-temple-red transition-colors"
title="{% trans 'Edit' %}">
<i data-lucide="edit-3" class="w-4 h-4"></i>
</a>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- Pagination -->
{% if page_obj.has_other_pages %}
<div class="px-6 py-4 border-t border-gray-200 bg-gray-50/50">
{% include "includes/paginator.html" %}
</div>
{% endif %}
</div>
{% else %}
<!-- Empty State -->
<div class="bg-white rounded-2xl shadow-sm border border-gray-200 py-12 sm:py-16">
<div class="flex flex-col items-center gap-6 text-center px-4">
<div class="w-24 h-24 rounded-full bg-temple-red/5 flex items-center justify-center empty-icon">
<i data-lucide="database" class="w-12 h-12 text-temple-red/50"></i>
</div>
<div class="max-w-md">
<h3 class="text-xl font-bold text-gray-900 mb-2">{% trans "No settings found" %}</h3>
<p class="text-gray-500">
{% 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>
</div>
<a href="{% url 'settings_create' %}"
class="btn-primary inline-flex items-center gap-2 px-8 py-4 bg-temple-red text-white rounded-xl font-semibold shadow-lg hover:shadow-xl">
<i data-lucide="plus-circle" class="w-5 h-5"></i>
{% trans "Create First Setting" %}
</a>
</div>
</div>
{% endif %}
</div>
{% endblock %}
{% block customJS %}
<script>
// Initialize Lucide icons
lucide.createIcons();
function toggleSecret(element) {
const fullValue = element.getAttribute('data-full-value');
const maskedValue = element.getAttribute('data-masked');
const icon = element.parentElement.parentElement.querySelector('[data-lucide="eye"], [data-lucide="eye-off"]');
if (element.textContent.trim() === maskedValue) {
element.textContent = fullValue;
element.classList.remove('text-gray-600', 'bg-gray-50');
element.classList.add('text-gray-900', 'bg-temple-red/5');
if (icon) icon.setAttribute('data-lucide', 'eye-off');
} else {
element.textContent = maskedValue;
element.classList.remove('text-gray-900', 'bg-temple-red/5');
element.classList.add('text-gray-600', 'bg-gray-50');
if (icon) icon.setAttribute('data-lucide', 'eye');
}
lucide.createIcons();
}
function copyToClipboard(text, btn) {
navigator.clipboard.writeText(text).then(() => {
const icon = btn.querySelector('i');
const originalLucide = icon.getAttribute('data-lucide');
icon.setAttribute('data-lucide', 'check-circle-2');
icon.classList.remove('text-gray-500', 'hover:text-temple-red');
icon.classList.add('text-green-500');
lucide.createIcons();
setTimeout(() => {
icon.setAttribute('data-lucide', originalLucide);
icon.classList.remove('text-green-500');
icon.classList.add('text-gray-500', 'hover:text-temple-red');
lucide.createIcons();
}, 2000);
});
}
</script>
{% endblock %}