Files
ipam/templates/subnet.html
T
2025-12-31 01:08:30 +00:00

304 lines
21 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ subnet.name }} - Subnet Details</title>
<link rel="icon" type="image/png" href="{{ LOGO_PNG }}">
<link href="/static/css/output.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css" rel="stylesheet">
</head>
<body class="bg-gray-300 text-gray-900 dark:bg-zinc-900 dark:text-gray-100 min-h-screen flex flex-col">
{% include 'header.html' %}
<div class="flex-1 flex items-center justify-center mx-4">
<div class="container py-8 w-full sm:max-w-3/4 pt-20">
<div class="flex items-center mb-6 relative">
<a href="/" class="hidden sm:flex absolute left-0 bg-gray-200 hover:bg-gray-400 dark:bg-zinc-700 dark:hover:bg-zinc-600 items-center justify-center rounded-full w-11 h-11"><i class="fas fa-arrow-left"></i></a>
<h1 class="text-3xl font-bold text-center w-full">{{ subnet.name }} ({{ subnet.cidr }})</h1>
<button type="button" id="export-csv" class="hidden sm:flex absolute right-0 bg-gray-200 hover:bg-gray-400 dark:bg-zinc-700 dark:hover:bg-zinc-600 hover:cursor-pointer items-center justify-center rounded-full w-11 h-11 export-csv-btn" title="Export as CSV" data-subnet-id="{{ subnet.id }}">
<i class="fas fa-file-csv fa-lg"></i>
</button>
</div>
<!-- Info Grid: 3 columns on desktop, 1 on mobile -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
<!-- Utilisation Stats Column -->
{% if utilization %}
<div class="bg-gray-200 dark:bg-zinc-800 p-4 rounded-lg">
<h3 class="text-lg font-bold mb-3 flex items-center gap-2">
<i class="fas fa-chart-pie"></i>
Utilisation
</h3>
<div class="space-y-2 text-sm">
<div class="flex justify-between">
<span class="text-gray-600 dark:text-gray-400">Used:</span>
<span class="font-medium">{{ utilization.percent }}%</span>
</div>
<div class="flex justify-between">
<span class="text-gray-600 dark:text-gray-400">Assigned:</span>
<span>{{ utilization.assigned }}</span>
</div>
<div class="flex justify-between">
<span class="text-gray-600 dark:text-gray-400">DHCP:</span>
<span>{{ utilization.dhcp }}</span>
</div>
<div class="flex justify-between">
<span class="text-gray-600 dark:text-gray-400">Available:</span>
<span>{{ utilization.available }}</span>
</div>
<div class="flex justify-between border-t border-gray-600 pt-2 mt-2">
<span class="text-gray-600 dark:text-gray-400">Total:</span>
<span class="font-medium">{{ utilization.total }}</span>
</div>
</div>
</div>
{% endif %}
<!-- VLAN Information Column -->
{% if subnet.vlan_id or subnet.vlan_description or subnet.vlan_notes %}
<div class="bg-gray-200 dark:bg-zinc-800 p-4 rounded-lg">
<h3 class="text-lg font-bold mb-3 flex items-center gap-2">
<i class="fas fa-network-wired"></i>
VLAN
</h3>
<div class="space-y-2 text-sm">
{% if subnet.vlan_id %}
<div>
<span class="text-gray-600 dark:text-gray-400">ID:</span>
<span class="ml-2 px-2 py-1 bg-gray-300 dark:bg-zinc-700 rounded text-sm font-mono">{{ subnet.vlan_id }}</span>
</div>
{% endif %}
{% if subnet.vlan_description %}
<div>
<span class="text-gray-600 dark:text-gray-400 block mb-1">Description:</span>
<span>{{ subnet.vlan_description }}</span>
</div>
{% endif %}
{% if subnet.vlan_notes %}
<div>
<span class="text-gray-600 dark:text-gray-400 block mb-1">Notes:</span>
<div class="whitespace-pre-wrap text-xs">{{ subnet.vlan_notes }}</div>
</div>
{% endif %}
</div>
</div>
{% endif %}
<!-- Custom Fields Column -->
{% if custom_fields %}
<div class="bg-gray-200 dark:bg-zinc-800 p-4 rounded-lg">
<h3 class="text-lg font-bold mb-3 flex items-center gap-2">
<i class="fas fa-list-ul"></i>
Custom Fields
</h3>
{% if can_edit_subnet %}
<form action="/subnet/{{ subnet.id }}/update_custom_fields" method="POST" id="custom-fields-form">
<div class="space-y-3">
{% for field in custom_fields %}
<div class="custom-field-item">
<label for="custom_field_{{ field.field_key }}" class="block mb-1 text-xs font-medium">
{{ field.name }}
{% if field.required %}<span class="text-red-500">*</span>{% endif %}
</label>
{% if field.field_type == 'textarea' %}
<textarea name="custom_field_{{ field.field_key }}"
id="custom_field_{{ field.field_key }}"
class="border p-2 rounded-lg bg-gray-300 dark:bg-zinc-900 border-gray-600 w-full resize-y text-sm"
{% if field.required %}required{% endif %}
placeholder="{{ field.help_text or '' }}">{{ field.current_value or field.default_value or '' }}</textarea>
{% elif field.field_type == 'boolean' %}
<div class="flex items-center space-x-2">
<input type="checkbox" name="custom_field_{{ field.field_key }}"
id="custom_field_{{ field.field_key }}"
value="true"
{% if field.current_value or (not field.current_value and field.default_value == 'true') %}checked{% endif %}
class="w-4 h-4">
<label for="custom_field_{{ field.field_key }}" class="text-sm">Yes</label>
</div>
{% elif field.field_type == 'select' %}
{% set options = [] %}
{% if field.validation_rules and field.validation_rules.select_options %}
{% set options = field.validation_rules.select_options %}
{% endif %}
<select name="custom_field_{{ field.field_key }}"
id="custom_field_{{ field.field_key }}"
class="border p-2 rounded-lg bg-gray-300 dark:bg-zinc-900 border-gray-600 w-full text-sm"
{% if field.required %}required{% endif %}>
{% if not field.required %}
<option value="">-- None --</option>
{% endif %}
{% for option in options %}
<option value="{{ option }}" {% if field.current_value == option or (not field.current_value and field.default_value == option) %}selected{% endif %}>{{ option }}</option>
{% endfor %}
</select>
{% elif field.field_type == 'date' %}
<input type="date" name="custom_field_{{ field.field_key }}"
id="custom_field_{{ field.field_key }}"
class="border p-2 rounded-lg bg-gray-300 dark:bg-zinc-900 border-gray-600 w-full text-sm"
value="{{ field.current_value or field.default_value or '' }}"
{% if field.required %}required{% endif %}>
{% elif field.field_type == 'datetime' %}
<input type="datetime-local" name="custom_field_{{ field.field_key }}"
id="custom_field_{{ field.field_key }}"
class="border p-2 rounded-lg bg-gray-300 dark:bg-zinc-900 border-gray-600 w-full text-sm"
value="{{ field.current_value or field.default_value or '' }}"
{% if field.required %}required{% endif %}>
{% elif field.field_type == 'number' %}
<input type="number" name="custom_field_{{ field.field_key }}"
id="custom_field_{{ field.field_key }}"
class="border p-2 rounded-lg bg-gray-300 dark:bg-zinc-900 border-gray-600 w-full text-sm"
value="{{ field.current_value or field.default_value or '' }}"
{% if field.required %}required{% endif %}
{% if field.validation_rules and field.validation_rules.min_value %}min="{{ field.validation_rules.min_value }}"{% endif %}
{% if field.validation_rules and field.validation_rules.max_value %}max="{{ field.validation_rules.max_value }}"{% endif %}>
{% elif field.field_type == 'decimal' %}
<input type="number" step="any" name="custom_field_{{ field.field_key }}"
id="custom_field_{{ field.field_key }}"
class="border p-2 rounded-lg bg-gray-300 dark:bg-zinc-900 border-gray-600 w-full text-sm"
value="{{ field.current_value or field.default_value or '' }}"
{% if field.required %}required{% endif %}
{% if field.validation_rules and field.validation_rules.min_value %}min="{{ field.validation_rules.min_value }}"{% endif %}
{% if field.validation_rules and field.validation_rules.max_value %}max="{{ field.validation_rules.max_value }}"{% endif %}>
{% elif field.field_type == 'ip_address' %}
<input type="text" name="custom_field_{{ field.field_key }}"
id="custom_field_{{ field.field_key }}"
class="border p-2 rounded-lg bg-gray-300 dark:bg-zinc-900 border-gray-600 w-full font-mono text-sm"
value="{{ field.current_value or field.default_value or '' }}"
placeholder="192.168.1.1"
{% if field.required %}required{% endif %}>
{% elif field.field_type == 'email' %}
<input type="email" name="custom_field_{{ field.field_key }}"
id="custom_field_{{ field.field_key }}"
class="border p-2 rounded-lg bg-gray-300 dark:bg-zinc-900 border-gray-600 w-full text-sm"
value="{{ field.current_value or field.default_value or '' }}"
placeholder="user@example.com"
{% if field.required %}required{% endif %}>
{% elif field.field_type == 'url' %}
<input type="url" name="custom_field_{{ field.field_key }}"
id="custom_field_{{ field.field_key }}"
class="border p-2 rounded-lg bg-gray-300 dark:bg-zinc-900 border-gray-600 w-full text-sm"
value="{{ field.current_value or field.default_value or '' }}"
placeholder="https://example.com"
{% if field.required %}required{% endif %}>
{% else %}
<input type="text" name="custom_field_{{ field.field_key }}"
id="custom_field_{{ field.field_key }}"
class="border p-2 rounded-lg bg-gray-300 dark:bg-zinc-900 border-gray-600 w-full text-sm"
value="{{ field.current_value or field.default_value or '' }}"
placeholder="{{ field.help_text or '' }}"
{% if field.required %}required{% endif %}
{% if field.validation_rules and field.validation_rules.min_length %}minlength="{{ field.validation_rules.min_length }}"{% endif %}
{% if field.validation_rules and field.validation_rules.max_length %}maxlength="{{ field.validation_rules.max_length }}"{% endif %}>
{% endif %}
</div>
{% endfor %}
</div>
</form>
{% else %}
<div class="space-y-3">
{% for field in custom_fields %}
<div class="custom-field-item">
<label class="block mb-1 text-xs font-medium">{{ field.name }}</label>
<div class="border p-2 rounded-lg bg-gray-300 dark:bg-zinc-900 border-gray-600 w-full text-sm">
{{ field.current_value or field.default_value or '-' }}
</div>
</div>
{% endfor %}
</div>
{% endif %}
</div>
{% endif %}
</div>
<!-- Actions Row -->
<div class="flex justify-center mb-4 gap-2">
<a href="/subnet/{{ subnet.id }}/dhcp" class="bg-gray-200 hover:bg-gray-400 dark:bg-zinc-700 dark:hover:bg-zinc-600 px-4 py-2 rounded-lg shadow items-center gap-2 flex">
<i class="fas fa-network-wired"></i> <span class="hidden sm:inline">Define </span>DHCP Pool
</a>
</div>
<button id="toggle-desc" class="sm:hidden bg-gray-300 hover:bg-gray-400 dark:bg-zinc-700 dark:hover:bg-zinc-600 hover:cursor-pointer px-4 py-2 rounded-lg mb-4 w-full">Show Descriptions</button>
<!-- IP Address Table -->
<form action="" method="POST">
<table class="table-auto w-full mb-6">
<thead>
<tr>
<th class="text-center text-gray-700 dark:text-gray-400">IP Address</th>
<th class="text-center text-gray-700 dark:text-gray-400">Hostname</th>
<th class="text-center text-gray-700 dark:text-gray-400 hidden sm:table-cell" id="desc-col-header">Description</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-700">
{% for ip in ip_addresses %}
<tr id="ip-{{ ip[0] }}">
<td class="font-bold text-center">
<button type="button" class="ip-history-btn hover:text-blue-400 cursor-pointer" data-ip="{{ ip[1] }}" title="View IP history">
{{ ip[1] }}
</button>
</td>
<td class="text-center">
{% if ip[2] == 'DHCP' %}
<span class="font-semibold">DHCP</span>
{% elif ip[2] and ip[3] %}
<a href="/device/{{ ip[3] }}" class="hover:text-blue-300">{{ ip[2] }}</a>
{% elif ip[2] %}
{{ ip[2] }}
{% else %}
{{ '' }}
{% endif %}
</td>
<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 '' %}
{% 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 %}
{% else %}
{# 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>
{% endfor %}
</tbody>
</table>
</form>
</div>
</div>
<!-- IP History Modal -->
<div id="ip-history-modal" class="hidden fixed inset-0 bg-black/30 backdrop-blur-md flex items-center justify-center z-50">
<div class="bg-gray-200 dark:bg-zinc-800 rounded-lg p-6 max-w-2xl w-full mx-4 max-h-[90vh] overflow-y-auto shadow-2xl border border-gray-300 dark:border-zinc-700">
<div class="flex justify-between items-center mb-4">
<h2 class="text-2xl font-bold">IP History: <span id="modal-ip-address" class="font-mono"></span></h2>
<button type="button" id="close-ip-history-modal" class="text-gray-600 dark:text-gray-400 hover:text-gray-800 dark:hover:text-gray-200 hover:cursor-pointer text-2xl">&times;</button>
</div>
<div id="ip-history-content" class="space-y-3">
<div class="text-center text-gray-600 dark:text-gray-400">Loading...</div>
</div>
</div>
</div>
<script src="/static/js/export_csv.min.js"></script>
<script src="/static/js/subnet.min.js"></script>
<script src="/static/js/ip_history.min.js"></script>
{% if can_edit_subnet %}
<script src="/static/js/subnet_custom_fields.min.js"></script>
{% endif %}
</body>
</html>