feat: feature flags

This commit is contained in:
2025-12-31 01:08:30 +00:00
parent 9558baf84e
commit c1b0a7084b
12 changed files with 393 additions and 78 deletions
+35 -1
View File
@@ -42,7 +42,7 @@
</div>
<i class="fas fa-chevron-right text-gray-400"></i>
</a>
{% if has_permission('view_tags') %}
{% if has_permission('view_tags') and is_feature_enabled('device_tags') %}
<a href="/tags" class="bg-gray-200 dark:bg-zinc-800 hover:bg-gray-300 dark:hover:bg-zinc-700 p-6 rounded-lg shadow-md flex items-center justify-between transition-colors">
<div class="flex items-center space-x-4">
<i class="fas fa-tags text-3xl text-gray-600 dark:text-gray-400"></i>
@@ -167,6 +167,40 @@
</div>
{% endif %}
</div>
<!-- Feature Flags Section -->
{% if has_permission('manage_users') %}
<div class="bg-gray-200 dark:bg-zinc-800 p-6 rounded-lg shadow-md mt-8">
<h2 class="text-2xl font-bold mb-6">Feature Flags</h2>
<form action="/admin/feature_flags" method="POST">
<div class="space-y-4">
{% for flag in feature_flags %}
<div class="flex items-center justify-between p-4 bg-gray-300 dark:bg-zinc-700 rounded-lg">
<div class="flex-1">
<label for="feature_{{ flag.key }}" class="text-lg font-semibold cursor-pointer">
{{ flag.key|replace('_', ' ')|title }}
</label>
{% if flag.description %}
<p class="text-sm text-gray-600 dark:text-gray-400 mt-1">{{ flag.description }}</p>
{% endif %}
</div>
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" name="feature_{{ flag.key }}" id="feature_{{ flag.key }}"
class="sr-only peer" {% if flag.enabled %}checked{% endif %}>
<div class="w-11 h-6 bg-gray-600 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-gray-800 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-gray-500 dark:peer-checked:bg-zinc-600"></div>
</label>
</div>
{% endfor %}
</div>
<div class="flex justify-end mt-6">
<button type="submit" class="bg-gray-300 hover:bg-gray-400 dark:bg-zinc-700 dark:hover:bg-zinc-600 hover:cursor-pointer px-6 py-2 rounded-lg font-medium">
Save Changes
</button>
</div>
</form>
</div>
{% endif %}
</div>
</div>
+4
View File
@@ -22,7 +22,9 @@
<div class="flex space-x-4">
<button onclick="showTab('assign-ips')" id="tab-assign-ips" class="tab-btn px-4 py-2 font-medium border-b-2 border-gray-600 text-gray-900 dark:text-gray-100 hover:cursor-pointer">Bulk IP Assignment</button>
<button onclick="showTab('create-devices')" id="tab-create-devices" class="tab-btn px-4 py-2 font-medium border-b-2 border-transparent text-gray-500 hover:text-gray-700 dark:hover:text-gray-300 hover:cursor-pointer">Bulk Device Creation</button>
{% if is_feature_enabled('device_tags') %}
<button onclick="showTab('assign-tags')" id="tab-assign-tags" class="tab-btn px-4 py-2 font-medium border-b-2 border-transparent text-gray-500 hover:text-gray-700 dark:hover:text-gray-300 hover:cursor-pointer">Bulk Tag Assignment</button>
{% endif %}
<button onclick="showTab('export')" id="tab-export" class="tab-btn px-4 py-2 font-medium border-b-2 border-transparent text-gray-500 hover:text-gray-700 dark:hover:text-gray-300 hover:cursor-pointer">Bulk Export</button>
</div>
</div>
@@ -94,6 +96,7 @@
</div>
<!-- Bulk Tag Assignment -->
{% if is_feature_enabled('device_tags') %}
<div id="panel-assign-tags" class="tab-panel hidden bg-gray-200 dark:bg-zinc-800 p-6 rounded-lg shadow-md">
{% if can_assign_device_tag %}
<h2 class="text-2xl font-bold mb-4">Bulk Tag Assignment</h2>
@@ -123,6 +126,7 @@
<p class="text-gray-500">You don't have permission to assign tags to devices.</p>
{% endif %}
</div>
{% endif %}
<!-- Bulk Export -->
<div id="panel-export" class="tab-panel hidden bg-gray-200 dark:bg-zinc-800 p-6 rounded-lg shadow-md">
+2
View File
@@ -115,6 +115,7 @@
{% endif %}
<!-- Tags Section -->
{% if is_feature_enabled('device_tags') %}
<div class="tags-section mb-6">
<h3 class="text-lg font-bold mb-2">Tags:</h3>
<div class="flex flex-wrap gap-2 mb-4">
@@ -155,6 +156,7 @@
</form>
{% endif %}
</div>
{% endif %}
<form action="/update_device_description" method="POST" class="mb-6 mt-4">
<input type="hidden" name="device_id" value="{{ device.id }}">
+5 -1
View File
@@ -16,14 +16,16 @@
<h1 class="text-3xl font-bold mb-6 text-center">Device Manager</h1>
<div class="flex flex-row justify-center gap-4 mb-6 flex-wrap">
<a href="/add_device" class="text-center bg-gray-200 hover:bg-gray-400 dark:bg-zinc-700 dark:hover:bg-zinc-600 px-4 py-2 rounded-lg">Add New Device</a>
{% if is_feature_enabled('bulk_operations') %}
<a href="/bulk" class="text-center bg-gray-200 hover:bg-gray-400 dark:bg-zinc-700 dark:hover:bg-zinc-600 px-4 py-2 rounded-lg">Bulk Operations</a>
{% endif %}
<a href="/device_type_stats" class="text-center bg-gray-200 hover:bg-gray-400 dark:bg-zinc-700 dark:hover:bg-zinc-600 px-4 py-2 rounded-lg">View Device Stats</a>
</div>
<!-- Filters Section -->
<div class="mb-6 space-y-4">
<!-- Tag Filter -->
{% if all_tag_names %}
{% if is_feature_enabled('device_tags') and all_tag_names %}
<div class="flex items-center space-x-2">
<label class="text-sm font-medium">Filter by tag:</label>
<select id="tag-filter" class="border p-2 rounded-lg bg-gray-200 dark:bg-zinc-800 border-gray-600">
@@ -67,6 +69,7 @@
</span>
</div>
<!-- Tags -->
{% if is_feature_enabled('device_tags') %}
{% set tags = device_tags.get(device.id, []) %}
{% if tags %}
<div class="flex flex-wrap gap-1 mt-2">
@@ -78,6 +81,7 @@
{% endfor %}
</div>
{% endif %}
{% endif %}
</a>
</li>
{% endfor %}
+2 -2
View File
@@ -29,7 +29,7 @@
<span>Devices</span>
</a>
{% endif %}
{% if has_permission('view_racks') %}
{% if has_permission('view_racks') and is_feature_enabled('racks') %}
<a href="/racks" class="text-gray-200 hover:text-gray-400 font-medium flex items-center gap-2">
<i class="fas fa-th"></i>
<span>Racks</span>
@@ -86,7 +86,7 @@
<span>Devices</span>
</a>
{% endif %}
{% if has_permission('view_racks') %}
{% if has_permission('view_racks') and is_feature_enabled('racks') %}
<a href="/racks" class="block px-6 py-2 text-gray-200 hover:text-gray-400 font-medium flex items-center gap-2">
<i class="fas fa-th"></i>
<span>Racks</span>
+18 -13
View File
@@ -251,21 +251,26 @@
<td class="text-left align-top hidden sm:table-cell desc-col">
{% set device_desc = ip[4] if ip[4] else '' %}
{% set ip_notes = ip[5] if ip|length > 5 and ip[5] else '' %}
{% set combined_desc = '' %}
{% if device_desc %}
{% set combined_desc = device_desc %}
{% endif %}
{% if ip_notes %}
{% if combined_desc %}
{% set combined_desc = combined_desc + '\n' + ip_notes %}
{% else %}
{% set combined_desc = ip_notes %}
{% if ip_notes_enabled %}
{% set combined_desc = '' %}
{% if device_desc %}
{% set combined_desc = device_desc %}
{% endif %}
{% if ip_notes %}
{% if combined_desc %}
{% set combined_desc = combined_desc + '\n' + ip_notes %}
{% else %}
{% set combined_desc = ip_notes %}
{% endif %}
{% endif %}
{% if can_edit_subnet %}
<textarea data-ip-id="{{ ip[0] }}" data-device-desc="{{ device_desc|e }}" rows="1" class="ip-notes-textarea border border-gray-600 rounded w-full p-2 bg-gray-200 dark:bg-zinc-800" style="overflow: hidden; resize: none;">{{ combined_desc }}</textarea>
{% else %}
<textarea readonly rows="1" class="border border-gray-600 rounded w-full cursor-pointer p-2" style="overflow: hidden; resize: none;">{{ combined_desc }}</textarea>
{% endif %}
{% endif %}
{% if can_edit_subnet %}
<textarea data-ip-id="{{ ip[0] }}" data-device-desc="{{ device_desc|e }}" rows="1" class="ip-notes-textarea border border-gray-600 rounded w-full p-2 bg-gray-200 dark:bg-zinc-800" style="overflow: hidden; resize: none;">{{ combined_desc }}</textarea>
{% else %}
<textarea readonly rows="1" class="border border-gray-600 rounded w-full cursor-pointer p-2" style="overflow: hidden; resize: none;">{{ combined_desc }}</textarea>
{# IP notes disabled - show only device description, read-only #}
<textarea readonly rows="1" class="border border-gray-600 rounded w-full cursor-pointer p-2 bg-gray-200 dark:bg-zinc-800" style="overflow: hidden; resize: none;">{{ device_desc }}</textarea>
{% endif %}
</td>
</tr>