refactor: Redone dark theme, added logging and gunicorn

This commit is contained in:
2025-07-24 11:15:42 +00:00
parent 6e068b6725
commit 480c79d6d8
23 changed files with 211 additions and 182 deletions
+5 -5
View File
@@ -8,23 +8,23 @@
<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-gray-900 dark:text-gray-100 min-h-screen flex flex-col">
<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 max-w-md pt-20">
<div class="flex items-center mb-6 relative">
<a href="javascript:window.history.back()" class="hidden sm:flex absolute left-0 bg-gray-200 hover:bg-gray-400 dark:bg-gray-700 dark:hover:bg-gray-600 items-center justify-center rounded-full w-11 h-11"><i class="fas fa-arrow-left"></i></a>
<a href="javascript:window.history.back()" 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">Add Device</h1>
</div>
<form action="/add_device" method="POST" class="flex flex-col space-y-4">
<input type="text" name="device_name" placeholder="Device Name" class="p-3 rounded-lg bg-gray-200 dark:bg-gray-800 border border-gray-600" required>
<input type="text" name="device_name" placeholder="Device Name" class="p-3 rounded-lg bg-gray-200 dark:bg-zinc-800 border border-gray-600" required>
<label for="device_type" class="block mb-2">Device Type</label>
<select id="device_type" name="device_type" class="p-2 rounded w-full mb-4 bg-gray-200 dark:bg-gray-800 border border-gray-600" required>
<select id="device_type" name="device_type" class="p-2 rounded w-full mb-4 bg-gray-200 dark:bg-zinc-800 border border-gray-600" required>
{% for dtype in device_types %}
<option value="{{ dtype[0] }}">{{ dtype[1] }}</option>
{% endfor %}
</select>
<button type="submit" class="bg-gray-200 hover:bg-gray-400 dark:bg-gray-700 dark:hover:bg-gray-600 px-4 py-2 rounded-lg">Add Device</button>
<button type="submit" class="bg-gray-200 hover:bg-gray-400 dark:bg-zinc-700 dark:hover:bg-zinc-600 px-4 py-2 rounded-lg">Add Device</button>
</form>
</div>
</div>
+7 -7
View File
@@ -8,28 +8,28 @@
<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-gray-900 dark:text-gray-100 min-h-screen flex flex-col">
<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 max-w-md pt-20">
<div class="flex items-center mb-6 relative">
<a href="/racks" class="absolute left-0 bg-gray-300 hover:bg-gray-400 dark:bg-gray-700 dark:hover:bg-gray-600 flex items-center justify-center rounded-full w-11 h-11"><i class="fas fa-arrow-left"></i></a>
<a href="/racks" class="absolute left-0 bg-gray-300 hover:bg-gray-400 dark:bg-zinc-700 dark:hover:bg-zinc-600 flex 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">Add Rack</h1>
</div>
<form action="/rack/add" method="POST" class="space-y-6 bg-gray-200 dark:bg-gray-800 rounded-lg shadow-md p-6">
<form action="/rack/add" method="POST" class="space-y-6 bg-gray-200 dark:bg-zinc-800 rounded-lg shadow-md p-6">
<div>
<label for="name" class="block font-medium mb-1">Rack Name</label>
<input type="text" id="name" name="name" class="p-3 rounded-lg bg-gray-200 dark:bg-gray-800 border border-gray-600 w-full" required>
<input type="text" id="name" name="name" class="p-3 rounded-lg bg-gray-200 dark:bg-zinc-800 border border-gray-600 w-full" required>
</div>
<div>
<label for="site" class="block font-medium mb-1">Site</label>
<input type="text" id="site" name="site" class="p-3 rounded-lg bg-gray-200 dark:bg-gray-800 border border-gray-600 w-full" required>
<input type="text" id="site" name="site" class="p-3 rounded-lg bg-gray-200 dark:bg-zinc-800 border border-gray-600 w-full" required>
</div>
<div>
<label for="height_u" class="block font-medium mb-1">Height (U)</label>
<input type="number" id="height_u" name="height_u" min="1" max="60" class="p-3 rounded-lg bg-gray-200 dark:bg-gray-800 border border-gray-600 w-full" required>
<input type="number" id="height_u" name="height_u" min="1" max="60" class="p-3 rounded-lg bg-gray-200 dark:bg-zinc-800 border border-gray-600 w-full" required>
</div>
<button type="submit" class="bg-gray-300 hover:bg-gray-400 dark:bg-gray-700 dark:hover:bg-gray-600 px-4 py-2 rounded-lg w-full">Add Rack</button>
<button type="submit" class="bg-gray-300 hover:bg-gray-400 dark:bg-zinc-700 dark:hover:bg-zinc-600 px-4 py-2 rounded-lg w-full">Add Rack</button>
</form>
</div>
</div>
+9 -9
View File
@@ -8,15 +8,15 @@
<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-gray-900 dark:text-gray-100 min-h-screen flex flex-col">
<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 max-w-md pt-20">
<div class="bg-gray-200 dark:bg-gray-800 rounded-2xl shadow-lg p-8">
<div class="bg-gray-200 dark:bg-zinc-800 rounded-2xl shadow-lg p-8">
<h1 class="text-3xl font-bold mb-6 text-center">Admin</h1>
<div class="flex justify-center gap-4 mb-6">
<a href="/audit" class="bg-gray-300 hover:bg-gray-400 dark:bg-gray-700 dark:hover:bg-gray-600 px-4 py-2 rounded-lg shadow text-center w-40">Audit Log</a>
<a href="/users" class="bg-gray-300 hover:bg-gray-400 dark:bg-gray-700 dark:hover:bg-gray-600 px-4 py-2 rounded-lg shadow text-center w-40">Users</a>
<a href="/audit" class="bg-gray-300 hover:bg-gray-400 dark:bg-zinc-700 dark:hover:bg-zinc-600 px-4 py-2 rounded-lg shadow text-center w-40">Audit Log</a>
<a href="/users" class="bg-gray-300 hover:bg-gray-400 dark:bg-zinc-700 dark:hover:bg-zinc-600 px-4 py-2 rounded-lg shadow text-center w-40">Users</a>
</div>
<hr class="border-t-2 border-gray-600 rounded-lg mb-4">
<h1 class="text-2xl font-bold mb-6 text-center">Add Subnet</h1>
@@ -25,17 +25,17 @@
{% endif %}
<form action="/add_subnet" method="POST" class="mb-6" onsubmit="return validateSubnetForm();">
<div class="flex flex-col space-y-4">
<input type="text" name="name" placeholder="Subnet Name" class="border p-3 rounded-lg bg-gray-300 dark:bg-gray-900 border-gray-600" required>
<input type="text" name="cidr" id="cidr-input" placeholder="CIDR (e.g., 192.168.1.0/24)" class="border p-3 rounded-lg bg-gray-300 dark:bg-gray-900 border-gray-600" required>
<input type="text" name="site" placeholder="Site/Location" class="border p-3 rounded-lg bg-gray-300 dark:bg-gray-900 border-gray-600" required>
<button type="submit" class="bg-gray-300 hover:bg-gray-400 dark:bg-gray-700 dark:hover:bg-gray-600 px-4 py-2 rounded-lg">Add Subnet</button>
<input type="text" name="name" placeholder="Subnet Name" class="border p-3 rounded-lg bg-gray-300 dark:bg-zinc-900 border-gray-600" required>
<input type="text" name="cidr" id="cidr-input" placeholder="CIDR (e.g., 192.168.1.0/24)" class="border p-3 rounded-lg bg-gray-300 dark:bg-zinc-900 border-gray-600" required>
<input type="text" name="site" placeholder="Site/Location" class="border p-3 rounded-lg bg-gray-300 dark:bg-zinc-900 border-gray-600" required>
<button type="submit" class="bg-gray-300 hover:bg-gray-400 dark:bg-zinc-700 dark:hover:bg-zinc-600 px-4 py-2 rounded-lg">Add Subnet</button>
<span id="cidr-error" class="text-red-500 text-sm hidden"></span>
</div>
</form>
<hr class="border-t-2 border-gray-600 rounded-lg mb-4">
<h1 class="text-2xl font-bold mb-6 text-center">Delete Subnet</h1>
<form action="/delete_subnet" method="POST" class="mb-6 flex items-center space-x-4 justify-center" onsubmit="return confirm('Are you sure you want to delete this subnet and all its IPs? This action is irreversible.');">
<select name="subnet_id" class="border p-3 rounded-lg bg-gray-300 dark:bg-gray-900 border-gray-600" required>
<select name="subnet_id" class="border p-3 rounded-lg bg-gray-300 dark:bg-zinc-900 border-gray-600" required>
<option value="" disabled selected>Select Subnet</option>
{% for subnet in subnets %}
<option value="{{ subnet.id }}">{{ subnet.name }} ({{ subnet.cidr }})</option>
+11 -11
View File
@@ -8,44 +8,44 @@
<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-gray-900 dark:text-gray-100 min-h-screen flex flex-col">
<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 max-w-8xl pt-20">
<h1 class="text-3xl font-bold mb-6 text-center">Audit Log</h1>
<form method="GET" class="flex flex-wrap gap-4 mb-6 justify-center">
<select name="user_id" class="border p-2 rounded-lg bg-gray-300 dark:bg-gray-900 border-gray-600">
<select name="user_id" class="border p-2 rounded-lg bg-gray-300 dark:bg-zinc-900 border-gray-600">
<option value="">All Users</option>
{% for user in users %}
<option value="{{ user[0] }}" {% if request.args.get('user_id') == user[0]|string %}selected{% endif %}>{{ user[1] }}</option>
{% endfor %}
</select>
<select name="subnet_id" class="border p-2 rounded-lg bg-gray-300 dark:bg-gray-900 border-gray-600">
<select name="subnet_id" class="border p-2 rounded-lg bg-gray-300 dark:bg-zinc-900 border-gray-600">
<option value="">All Subnets</option>
{% for subnet in subnets %}
<option value="{{ subnet[0] }}" {% if request.args.get('subnet_id') == subnet[0]|string %}selected{% endif %}>{{ subnet[1] }}</option>
{% endfor %}
</select>
<select name="action" class="border p-2 rounded-lg bg-gray-300 dark:bg-gray-900 border-gray-600">
<select name="action" class="border p-2 rounded-lg bg-gray-300 dark:bg-zinc-900 border-gray-600">
<option value="">All Actions</option>
{% for a in actions %}
<option value="{{ a }}" {% if request.args.get('action') == a %}selected{% endif %}>{{ a }}</option>
{% endfor %}
</select>
<select name="device_name" class="border p-2 rounded-lg bg-gray-300 dark:bg-gray-900 border-gray-600">
<select name="device_name" class="border p-2 rounded-lg bg-gray-300 dark:bg-zinc-900 border-gray-600">
<option value="">All Devices</option>
{% for device in devices %}
<option value="{{ device[0] }}" {% if request.args.get('device_name') == device[0] %}selected{% endif %}>{{ device[0] }}</option>
{% endfor %}
</select>
<button type="submit" class="bg-gray-200 hover:bg-gray-400 dark:bg-gray-700 dark:hover:bg-gray-600 px-4 py-2 rounded-lg flex items-center gap-2">
<button type="submit" class="bg-gray-200 hover:bg-gray-400 dark:bg-zinc-700 dark:hover:bg-zinc-600 px-4 py-2 rounded-lg flex items-center gap-2">
<i class="fas fa-search"></i>
<span>Filter</span>
</button>
</form>
<table class="w-full table-auto bg-gray-200 dark:bg-gray-800 rounded-lg overflow-hidden">
<table class="w-full table-auto bg-gray-200 dark:bg-zinc-800 rounded-lg overflow-hidden">
<thead>
<tr class="bg-gray-400 dark:bg-gray-700">
<tr class="bg-gray-400 dark:bg-zinc-700">
<th class="px-4 py-2 text-center">User</th>
<th class="px-4 py-2 text-center">Action</th>
<th class="px-4 py-2 text-center">Details</th>
@@ -70,7 +70,7 @@
{% if page > 1 %}
{% set prev_args = query_args.copy() %}
{% set _ = prev_args.update({'page': page-1}) %}
<a href="{{ url_for('audit', **prev_args) }}" class="px-3 py-1 rounded bg-gray-400 hover:bg-gray-200 dark:bg-gray-700 dark:hover:bg-gray-600 flex items-center gap-2">
<a href="{{ url_for('audit', **prev_args) }}" class="px-3 py-1 rounded bg-gray-400 hover:bg-gray-200 dark:bg-zinc-700 dark:hover:bg-zinc-600 flex items-center gap-2">
<i class="fa fa-angle-left"></i>
<span class="hidden sm:inline">Prev</span>
</a>
@@ -78,12 +78,12 @@
{% for p in range(1, total_pages+1) %}
{% set page_args = query_args.copy() %}
{% set _ = page_args.update({'page': p}) %}
<a href="{{ url_for('audit', **page_args) }}" class="px-3 py-1 rounded {{ 'bg-gray-200 dark:bg-gray-500' if p == page else 'bg-gray-400 hover:bg-gray-200 dark:bg-gray-700 dark:hover:bg-gray-600' }}">{{ p }}</a>
<a href="{{ url_for('audit', **page_args) }}" class="px-3 py-1 rounded {{ 'bg-gray-200 dark:bg-gray-500' if p == page else 'bg-gray-400 hover:bg-gray-200 dark:bg-zinc-700 dark:hover:bg-zinc-600' }}">{{ p }}</a>
{% endfor %}
{% if page < total_pages %}
{% set next_args = query_args.copy() %}
{% set _ = next_args.update({'page': page+1}) %}
<a href="{{ url_for('audit', **next_args) }}" class="px-3 py-1 rounded bg-gray-400 hover:bg-gray-200 dark:bg-gray-700 dark:hover:bg-gray-600 flex items-center gap-2">
<a href="{{ url_for('audit', **next_args) }}" class="px-3 py-1 rounded bg-gray-400 hover:bg-gray-200 dark:bg-zinc-700 dark:hover:bg-zinc-600 flex items-center gap-2">
<span class="hidden sm:inline">Next</span>
<i class="fa fa-angle-right"></i>
</a>
+11 -11
View File
@@ -8,16 +8,16 @@
<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-gray-900 dark:text-gray-100 min-h-screen flex flex-col">
<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-auto min-w-[20rem] max-w-2xl pt-20">
<div class="flex items-center mb-8 relative justify-between gap-4">
<a href="javascript:window.history.back()" class="hidden sm:flex bg-gray-200 hover:bg-gray-400 dark:bg-gray-700 dark:hover:bg-gray-600 flex items-center justify-center rounded-full w-11 h-11 shrink-0"><i class="fas fa-arrow-left"></i></a>
<a href="javascript:window.history.back()" class="hidden sm:flex bg-gray-200 hover:bg-gray-400 dark:bg-zinc-700 dark:hover:bg-zinc-600 flex items-center justify-center rounded-full w-11 h-11 shrink-0"><i class="fas fa-arrow-left"></i></a>
<h1 class="text-3xl font-bold text-center flex-1 min-w-0 truncate">{{ device.name }}</h1>
<form action="/update_device_type" method="POST" class="hidden md:inline ml-2">
<input type="hidden" name="device_id" value="{{ device.id }}">
<select name="device_type_id" class="border p-2 rounded bg-gray-200 dark:bg-gray-800 border-gray-600" onchange="this.form.submit()">
<select name="device_type_id" class="border p-2 rounded bg-gray-200 dark:bg-zinc-800 border-gray-600" onchange="this.form.submit()">
{% for dtype in device_types %}
<option value="{{ dtype[0] }}" {% if device.device_type_id == dtype[0] %}selected{% endif %}>{{ dtype[1] }}</option>
{% endfor %}
@@ -26,7 +26,7 @@
<div class="flex items-center shrink-0">
<form action="/rename_device" method="POST" class="inline">
<input type="hidden" name="device_id" value="{{ device.id }}">
<input type="text" name="new_name" value="{{ device.name }}" class="hidden border p-1 rounded bg-gray-200 dark:bg-gray-800 border-gray-600 w-32 mr-2" style="vertical-align: middle;" required>
<input type="text" name="new_name" value="{{ device.name }}" class="hidden border p-1 rounded bg-gray-200 dark:bg-zinc-800 border-gray-600 w-32 mr-2" style="vertical-align: middle;" required>
<button type="button" class="text-blue-400 hover:text-blue-600 ml-2 rename-btn" title="Rename Device"><i class="fas fa-pencil-alt"></i></button>
<button type="submit" class="text-green-400 hover:text-green-600 ml-2 save-btn hidden" title="Save Name"><i class="fas fa-check"></i></button>
<button type="button" class="text-gray-400 hover:text-gray-600 ml-2 cancel-btn hidden" title="Cancel"><i class="fas fa-times"></i></button>
@@ -41,30 +41,30 @@
</div>
<form action="/device/{{ device.id }}/add_ip" method="POST" class="mb-6">
<div class="flex flex-col space-y-4">
<select name="site" id="site-select" class="border p-3 rounded-lg bg-gray-200 dark:bg-gray-800 border-gray-600 w-full" required>
<select name="site" id="site-select" class="border p-3 rounded-lg bg-gray-200 dark:bg-zinc-800 border-gray-600 w-full" required>
<option value="" disabled selected>Select Site...</option>
{% set sites = subnets | map(attribute='site') | unique | list %}
{% for site in sites %}
<option value="{{ site }}">{{ site }}</option>
{% endfor %}
</select>
<select name="subnet_id" id="subnet-select" class="border p-3 rounded-lg bg-gray-200 dark:bg-gray-800 border-gray-600 w-full" required>
<select name="subnet_id" id="subnet-select" class="border p-3 rounded-lg bg-gray-200 dark:bg-zinc-800 border-gray-600 w-full" required>
<option value="" disabled selected>Select Subnet...</option>
{% for subnet in subnets %}
<option value="{{ subnet.id }}" data-site="{{ subnet.site }}">{{ subnet.name }} ({{ subnet.cidr }})</option>
{% endfor %}
</select>
<select name="ip_id" id="ip-select" class="border p-3 rounded-lg bg-gray-200 dark:bg-gray-800 border-gray-600 w-full" required>
<select name="ip_id" id="ip-select" class="border p-3 rounded-lg bg-gray-200 dark:bg-zinc-800 border-gray-600 w-full" required>
<option value="" disabled selected>Select IP...</option>
</select>
<button type="submit" class="bg-gray-200 hover:bg-gray-400 dark:bg-gray-700 dark:hover:bg-gray-600 px-4 py-2 rounded-lg w-full">Add IP</button>
<button type="submit" class="bg-gray-200 hover:bg-gray-400 dark:bg-zinc-700 dark:hover:bg-zinc-600 px-4 py-2 rounded-lg w-full">Add IP</button>
</div>
</form>
<div class="allocated-ips">
<h3 class="text-lg font-bold mb-2">Allocated IPs:</h3>
<ul class="space-y-2">
{% for ip in device_ips %}
<li class="flex justify-between items-center bg-gray-200 dark:bg-gray-700 p-2 rounded-lg">
<li class="flex justify-between items-center bg-gray-200 dark:bg-zinc-700 p-2 rounded-lg">
<span class="allocated-ip">{{ ip.ip }}</span>
<form action="/device/{{ device.id }}/delete_ip" method="POST" class="inline">
<input type="hidden" name="device_ip_id" value="{{ ip.device_ip_id }}">
@@ -77,8 +77,8 @@
<form action="/update_device_description" method="POST" class="mb-6 mt-4">
<input type="hidden" name="device_id" value="{{ device.id }}">
<label for="description" class="block mb-2 text-lg font-bold">Description</label>
<textarea id="description" name="description" rows="3" class="border p-2 rounded-lg bg-gray-200 dark:bg-gray-800 border-gray-600 w-full resize-y" placeholder="Enter device description...">{{ device.description or '' }}</textarea>
<button type="submit" class="bg-gray-200 hover:bg-gray-400 dark:bg-gray-700 dark:hover:bg-gray-600 px-4 py-2 rounded-lg w-full mt-2">Save Description</button>
<textarea id="description" name="description" rows="3" class="border p-2 rounded-lg bg-gray-200 dark:bg-zinc-800 border-gray-600 w-full resize-y" placeholder="Enter device description...">{{ device.description or '' }}</textarea>
<button type="submit" class="bg-gray-200 hover:bg-gray-400 dark:bg-zinc-700 dark:hover:bg-zinc-600 px-4 py-2 rounded-lg w-full mt-2">Save Description</button>
</form>
</div>
</div>
+5 -5
View File
@@ -8,18 +8,18 @@
<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-gray-900 dark:text-gray-100 min-h-screen flex flex-col">
<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 max-w-2xl pt-20">
<div class="flex items-center mb-6 relative">
<a href="/devices" class="hidden sm:flex absolute left-0 bg-gray-200 hover:bg-gray-400 dark:bg-gray-700 dark:hover:bg-gray-600 items-center justify-center rounded-full w-11 h-11"><i class="fas fa-arrow-left"></i></a>
<a href="/devices" 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">Device Stats</h1>
</div>
<div class="bg-gray-200 dark:bg-gray-800 rounded-lg shadow-md p-6">
<div class="bg-gray-200 dark:bg-zinc-800 rounded-lg shadow-md p-6">
<table class="w-full table-auto">
<thead>
<tr class="dark:bg-gray-700">
<tr class="dark:bg-zinc-700">
<th class="px-4 py-2 text-left">Type</th>
<th class="px-4 py-2 text-center">Count</th>
</tr>
@@ -29,7 +29,7 @@
<tr class="border-b border-gray-700">
<td class="px-4 py-3 flex items-center gap-2">
<a href="{{ url_for('devices_by_type', device_type=name) }}" class="hover:underline flex items-center gap-2">
<i class="fas {{ icon_class }} dark:text-blue-300"></i> {{ name }}
<i class="fas {{ icon_class }} dark:text-white"></i> {{ name }}
</a>
</td>
<td class="px-4 py-3 text-center font-bold">{{ count }}</td>
+8 -8
View File
@@ -9,23 +9,23 @@
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css" rel="stylesheet">
<link href="/static/css/devices.css" rel="stylesheet">
</head>
<body class="bg-gray-300 text-gray-900 dark:bg-gray-900 dark:text-gray-100 min-h-screen flex flex-col">
<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 max-w-4xl pt-20">
<h1 class="text-3xl font-bold mb-6 text-center">Device Manager</h1>
<div class="flex flex-row justify-center gap-4 mb-6">
<a href="/add_device" class="text-center bg-gray-200 hover:bg-gray-400 dark:bg-gray-700 dark:hover:bg-gray-600 px-4 py-2 rounded-lg">Add New Device</a>
<a href="/device_type_stats" class="text-center bg-gray-200 hover:bg-gray-400 dark:bg-gray-700 dark:hover:bg-gray-600 px-4 py-2 rounded-lg">View Device Stats</a>
<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>
<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>
<div class="mb-6">
<input type="text" id="search" placeholder="Search devices or IPs..." class="border p-3 rounded-lg bg-gray-200 dark:bg-gray-800 border-gray-600 w-full">
<input type="text" id="search" placeholder="Search devices or IPs..." class="border p-3 rounded-lg bg-gray-200 dark:bg-zinc-800 border-gray-600 w-full">
</div>
<div id="site-list" class="space-y-6">
{% for site, devices in sites_devices.items() %}
<div class="site-group bg-gray-200 dark:bg-gray-800 rounded-lg shadow-md">
<div class="site-group bg-gray-200 dark:bg-zinc-800 rounded-lg shadow-md">
<div class="flex flex-row items-center justify-between p-4 cursor-pointer site-header">
<h2 class="text-xl font-bold mb-0 dark:text-blue-300">{{ site }}</h2>
<h2 class="text-xl font-bold mb-0 dark:text-white">{{ site }}</h2>
<button type="button" class="expand-btn text-gray-400 hover:text-gray-200 ml-2 flex items-center" aria-label="Expand site">
<i class="fas fa-chevron-down"></i>
</button>
@@ -33,13 +33,13 @@
<ul class="device-list hidden px-6 pb-4">
{% for device in devices %}
<li class="my-2">
<a href="/device/{{ device.id }}" class="flex items-center justify-between bg-gray-300 hover:bg-gray-100 dark:bg-gray-900 dark:hover:bg-gray-700 dark:text-gray-200 font-semibold rounded-lg px-4 py-2 shadow transition-colors duration-150">
<a href="/device/{{ device.id }}" class="flex items-center justify-between bg-gray-300 hover:bg-gray-100 dark:bg-zinc-900 dark:hover:bg-gray-700 dark:text-gray-200 font-semibold rounded-lg px-4 py-2 shadow transition-colors duration-150">
<span><i class="fas {{ device.icon_class or 'fa-server' }} mr-2"></i>{{ device.name }}</span>
{% set ips = device_ips.get(device.id, []) %}
<span class="flex flex-row flex-wrap justify-end items-center ml-4 text-xs text-blue-300 font-normal align-middle">
{% if ips|length > 0 %}
{% for ip in ips %}
<span class="inline-block bg-gray-200 dark:bg-gray-800 text-gray-900 dark:text-blue-200 rounded px-2 py-1 ml-1 font-mono">{{ ip[1] }}</span>
<span class="inline-block bg-gray-200 dark:bg-zinc-800 text-gray-900 dark:text-white rounded px-2 py-1 ml-1 font-mono">{{ ip[1] }}</span>
{% endfor %}
{% else %}
<span class="text-gray-400">No IPs</span>
+7 -7
View File
@@ -8,23 +8,23 @@
<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-gray-900 dark:text-gray-100 min-h-screen flex flex-col">
<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 max-w-3xl pt-20">
<div class="flex items-center mb-6 relative">
<a href="/device_type_stats" class="absolute left-0 bg-gray-200 hover:bg-gray-400 dark:bg-gray-700 dark:hover:bg-gray-600 flex items-center justify-center rounded-full w-11 h-11"><i class="fas fa-arrow-left"></i></a>
<a href="/device_type_stats" class="absolute left-0 bg-gray-200 hover:bg-gray-400 dark:bg-zinc-700 dark:hover:bg-zinc-600 flex 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">
<i class="fas {{ icon_class }}"></i> {{ device_type }} Devices
</h1>
</div>
{% for site, devices in site_devices.items() %}
<div class="mb-8">
<h2 class="text-xl font-bold mb-4 dark:text-blue-200">{{ site }}</h2>
<div class="bg-gray-200 dark:bg-gray-800 rounded-lg shadow-md p-4">
<h2 class="text-xl font-bold mb-4 dark:text-white">{{ site }}</h2>
<div class="bg-gray-200 dark:bg-zinc-800 rounded-lg shadow-md p-4">
<table class="w-full table-auto mb-2">
<thead>
<tr class="bg-gray-200 dark:bg-gray-700">
<tr class="bg-gray-200 dark:bg-zinc-700">
<th class="px-4 py-2 text-left">Device Name</th>
<th class="px-4 py-2 text-left">Description</th>
</tr>
@@ -33,14 +33,14 @@
{% for device in devices %}
<tr class="border-b border-gray-700">
<td class="px-4 py-3">
<a href="/device/{{ device.id }}" class="hover:underline dark:text-blue-300">{{ device.name }}</a>
<a href="/device/{{ device.id }}" class="hover:underline dark:text-white">{{ device.name }}</a>
</td>
<td class="px-4 py-3">{{ device.description or '' }}</td>
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr class="bg-gray-200 dark:bg-gray-900 font-bold">
<tr class="bg-gray-200 dark:bg-zinc-900 font-bold">
<td class="px-4 py-2 text-right" colspan="2">Total: {{ devices|length }}</td>
</tr>
</tfoot>
+9 -9
View File
@@ -8,33 +8,33 @@
<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-gray-900 dark:text-gray-100 min-h-screen flex flex-col">
<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="/subnet/{{ subnet.id }}" class="absolute left-0 bg-gray-300 hover:bg-gray-400 dark:bg-gray-700 dark:hover:bg-gray-600 flex items-center justify-center rounded-full w-11 h-11"><i class="fas fa-arrow-left"></i></a>
<a href="/subnet/{{ subnet.id }}" class="absolute left-0 bg-gray-300 hover:bg-gray-400 dark:bg-zinc-700 dark:hover:bg-zinc-600 flex 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">DHCP Pool for {{ subnet.name }}</h1>
</div>
{% if error %}
<div class="text-red-500 text-center mb-4">{{ error }}</div>
{% endif %}
<form action="" method="POST" class="bg-gray-200 dark:bg-gray-800 p-6 rounded-lg shadow-md flex flex-col gap-4">
<form action="" method="POST" class="bg-gray-200 dark:bg-zinc-800 p-6 rounded-lg shadow-md flex flex-col gap-4">
<label for="start_ip" class="font-medium">Start IP Address</label>
<input type="text" id="start_ip" name="start_ip" class="border p-3 rounded-lg bg-gray-300 text-gray-900 dark:bg-gray-900 dark:text-gray-100 border-gray-600" placeholder="e.g. 192.168.1.100" required value="{{ dhcp_pool.start_ip if dhcp_pool else '' }}">
<input type="text" id="start_ip" name="start_ip" class="border p-3 rounded-lg bg-gray-300 text-gray-900 dark:bg-zinc-900 dark:text-gray-100 border-gray-600" placeholder="e.g. 192.168.1.100" required value="{{ dhcp_pool.start_ip if dhcp_pool else '' }}">
<label for="end_ip" class="font-medium">End IP Address</label>
<input type="text" id="end_ip" name="end_ip" class="border p-3 rounded-lg bg-gray-300 text-gray-900 dark:bg-gray-900 dark:text-gray-100 border-gray-600" placeholder="e.g. 192.168.1.200" required value="{{ dhcp_pool.end_ip if dhcp_pool else '' }}">
<input type="text" id="end_ip" name="end_ip" class="border p-3 rounded-lg bg-gray-300 text-gray-900 dark:bg-zinc-900 dark:text-gray-100 border-gray-600" placeholder="e.g. 192.168.1.200" required value="{{ dhcp_pool.end_ip if dhcp_pool else '' }}">
<label for="excluded_ips" class="font-medium">Exclude IPs (comma separated)</label>
<input type="text" id="excluded_ips" name="excluded_ips" class="border p-3 rounded-lg bg-gray-300 text-gray-900 dark:bg-gray-900 dark:text-gray-100 border-gray-600" placeholder="e.g. 192.168.1.105,192.168.1.110" value="{{ dhcp_pool.excluded_ips if dhcp_pool and dhcp_pool.excluded_ips else '' }}">
<input type="text" id="excluded_ips" name="excluded_ips" class="border p-3 rounded-lg bg-gray-300 text-gray-900 dark:bg-zinc-900 dark:text-gray-100 border-gray-600" placeholder="e.g. 192.168.1.105,192.168.1.110" value="{{ dhcp_pool.excluded_ips if dhcp_pool and dhcp_pool.excluded_ips else '' }}">
<div class="flex gap-4 mt-4">
<button type="submit" class="bg-gray-300 hover:bg-gray-400 dark:bg-gray-700 dark:hover:bg-gray-600 px-4 py-2 rounded-lg">Save DHCP Pool</button>
<button type="submit" class="bg-gray-300 hover:bg-gray-400 dark:bg-zinc-700 dark:hover:bg-zinc-600 px-4 py-2 rounded-lg">Save DHCP Pool</button>
{% if dhcp_pool %}
<button type="submit" name="remove" value="1" class="bg-gray-300 hover:bg-gray-400 dark:bg-gray-700 dark:hover:bg-gray-600 px-4 py-2 rounded-lg">Remove DHCP Pool</button>
<button type="submit" name="remove" value="1" class="bg-gray-300 hover:bg-gray-400 dark:bg-zinc-700 dark:hover:bg-zinc-600 px-4 py-2 rounded-lg">Remove DHCP Pool</button>
{% endif %}
</div>
</form>
{% if dhcp_pool %}
<div class="mt-8 bg-gray-200 dark:bg-gray-800 p-4 rounded-lg">
<div class="mt-8 bg-gray-200 dark:bg-zinc-800 p-4 rounded-lg">
<h2 class="text-xl font-bold mb-2">Current DHCP Pool</h2>
<div>Start: <span class="font-mono">{{ dhcp_pool.start_ip }}</span></div>
<div>End: <span class="font-mono">{{ dhcp_pool.end_ip }}</span></div>
+16 -16
View File
@@ -1,4 +1,4 @@
<header class="bg-gray-800 shadow-md py-3 px-6 flex items-center justify-between relative">
<header class="bg-zinc-800 shadow-md py-3 px-6 flex items-center justify-between relative">
<a href="/" class="flex items-center space-x-3">
<img src="{{ LOGO_PNG }}" alt="Logo" class="h-8 rounded">
<span class="text-2xl font-bold text-white">{{ NAME }} IPAM</span>
@@ -7,14 +7,14 @@
{% if current_user_name %}Hello, {{ current_user_name }}{% endif %}
</div>
<nav class="hidden md:flex items-center space-x-6" id="main-nav">
<a href="/" class="text-gray-200 hover:text-blue-400 font-medium">Home</a>
<a href="/devices" class="text-gray-200 hover:text-blue-400 font-medium">Devices</a>
<a href="/racks" class="text-gray-200 hover:text-blue-400 font-medium">Racks</a>
<a href="/admin" class="text-gray-200 hover:text-blue-400 font-medium">Admin</a>
<a href="/audit" class="text-gray-200 hover:text-blue-400 font-medium">Audit Log</a>
<a href="/help" class="text-gray-200 hover:text-blue-400 font-medium">Help</a>
<a href="/" class="text-gray-200 hover:text-gray-400 font-medium">Home</a>
<a href="/devices" class="text-gray-200 hover:text-gray-400 font-medium">Devices</a>
<a href="/racks" class="text-gray-200 hover:text-gray-400 font-medium">Racks</a>
<a href="/admin" class="text-gray-200 hover:text-gray-400 font-medium">Admin</a>
<a href="/audit" class="text-gray-200 hover:text-gray-400 font-medium">Audit Log</a>
<a href="/help" class="text-gray-200 hover:text-gray-400 font-medium">Help</a>
{% if current_user_name %}
<a href="/logout" class="text-gray-200 hover:text-blue-400 font-medium">Logout</a>
<a href="/logout" class="text-gray-200 hover:text-gray-400 font-medium">Logout</a>
{% endif %}
</nav>
<button class="md:hidden flex items-center text-gray-200 focus:outline-none" id="nav-toggle" aria-label="Open navigation menu">
@@ -22,15 +22,15 @@
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path>
</svg>
</button>
<div class="md:hidden absolute top-16 right-6 bg-gray-800 rounded-lg shadow-lg z-50 w-48 hidden flex-col py-2" id="mobile-nav">
<a href="/" class="block px-6 py-2 text-gray-200 hover:text-blue-400 font-medium">Home</a>
<a href="/devices" class="block px-6 py-2 text-gray-200 hover:text-blue-400 font-medium">Devices</a>
<a href="/racks" class="block px-6 py-2 text-gray-200 hover:text-blue-400 font-medium">Racks</a>
<a href="/admin" class="block px-6 py-2 text-gray-200 hover:text-blue-400 font-medium">Admin</a>
<a href="/audit" class="block px-6 py-2 text-gray-200 hover:text-blue-400 font-medium">Audit Log</a>
<a href="/help" class="block px-6 py-2 text-gray-200 hover:text-blue-400 font-medium">Help</a>
<div class="md:hidden absolute top-16 right-6 bg-zinc-800 rounded-lg shadow-lg z-50 w-48 hidden flex-col py-2" id="mobile-nav">
<a href="/" class="block px-6 py-2 text-gray-200 hover:text-gray-400 font-medium">Home</a>
<a href="/devices" class="block px-6 py-2 text-gray-200 hover:text-gray-400 font-medium">Devices</a>
<a href="/racks" class="block px-6 py-2 text-gray-200 hover:text-gray-400 font-medium">Racks</a>
<a href="/admin" class="block px-6 py-2 text-gray-200 hover:text-gray-400 font-medium">Admin</a>
<a href="/audit" class="block px-6 py-2 text-gray-200 hover:text-gray-400 font-medium">Audit Log</a>
<a href="/help" class="block px-6 py-2 text-gray-200 hover:text-gray-400 font-medium">Help</a>
{% if current_user_name %}
<a href="/logout" class="block px-6 py-2 text-gray-200 hover:text-blue-400 font-medium">Logout</a>
<a href="/logout" class="block px-6 py-2 text-gray-200 hover:text-gray-400 font-medium">Logout</a>
{% endif %}
</div>
<script src="/static/js/header.js"></script>
+8 -8
View File
@@ -8,26 +8,26 @@
<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-gray-900 dark:text-gray-100 min-h-screen flex flex-col">
<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 max-w-2xl pt-20">
<h1 class="text-3xl font-bold mb-8 text-center">Help & User Guide</h1>
<div class="space-y-10 text-lg">
<div class="bg-gray-200 dark:bg-gray-800 rounded-2xl shadow-lg p-8">
<div class="bg-gray-200 dark:bg-zinc-800 rounded-2xl shadow-lg p-8">
<h2 class="text-2xl font-semibold mb-4 border-b border-gray-700 pb-2">Subnets & Devices</h2>
<div class="space-y-4">
<div>
<h3 class="text-xl font-semibold mb-1">Managing Subnets</h3>
<p>To add or remove subnets, go to the <a href="/admin" class="text-blue-600 dark:text-blue-400 hover:underline">Admin</a> page. Click <span class="bg-gray-300 dark:bg-gray-700 px-2 py-1 rounded">Add Subnet</span> to create a new subnet. Subnets are associated with sites.</p>
<p>To add or remove subnets, go to the <a href="/admin" class="text-blue-600 dark:text-blue-400 hover:underline">Admin</a> page. Click <span class="bg-gray-300 dark:bg-zinc-700 px-2 py-1 rounded">Add Subnet</span> to create a new subnet. Subnets are associated with sites.</p>
</div>
<div>
<h3 class="text-xl font-semibold mb-1">Adding a Device</h3>
<p>To add a device, visit the <a href="/devices" class="text-blue-600 dark:text-blue-400 hover:underline">Devices</a> page and click <span class="bg-gray-300 dark:bg-gray-700 px-2 py-1 rounded">Add Device</span>.</p>
<p>To add a device, visit the <a href="/devices" class="text-blue-600 dark:text-blue-400 hover:underline">Devices</a> page and click <span class="bg-gray-300 dark:bg-zinc-700 px-2 py-1 rounded">Add Device</span>.</p>
</div>
<div>
<h3 class="text-xl font-semibold mb-1">Assigning IP Addresses</h3>
<p>To assign an IP address, you must first add a device. Then, from the <a href="/devices" class="text-blue-600 dark:text-blue-400 hover:underline">Devices</a> page, click on a device to view its details and use the <span class="bg-gray-300 dark:bg-gray-700 px-2 py-1 rounded">Add IP</span> option to assign an IP address from a subnet.</p>
<p>To assign an IP address, you must first add a device. Then, from the <a href="/devices" class="text-blue-600 dark:text-blue-400 hover:underline">Devices</a> page, click on a device to view its details and use the <span class="bg-gray-300 dark:bg-zinc-700 px-2 py-1 rounded">Add IP</span> option to assign an IP address from a subnet.</p>
</div>
<div>
<h3 class="text-xl font-semibold mb-1">Viewing and Editing Devices</h3>
@@ -35,12 +35,12 @@
</div>
</div>
</div>
<div class="bg-gray-200 dark:bg-gray-800 rounded-2xl shadow-lg p-8">
<div class="bg-gray-200 dark:bg-zinc-800 rounded-2xl shadow-lg p-8">
<h2 class="text-2xl font-semibold mb-4 border-b border-gray-700 pb-2">Racks</h2>
<div class="space-y-4">
<div>
<h3 class="text-xl font-semibold mb-1">Adding a Rack</h3>
<p>To add a new rack, go to the <a href="/racks" class="text-blue-600 dark:text-blue-400 hover:underline">Racks</a> page and click the <span class="bg-gray-300 dark:bg-gray-700 px-2 py-1 rounded">Add Rack</span> button. Fill in the details and submit the form.</p>
<p>To add a new rack, go to the <a href="/racks" class="text-blue-600 dark:text-blue-400 hover:underline">Racks</a> page and click the <span class="bg-gray-300 dark:bg-zinc-700 px-2 py-1 rounded">Add Rack</span> button. Fill in the details and submit the form.</p>
</div>
<div>
<h3 class="text-xl font-semibold mb-1">Assigning Devices to Racks</h3>
@@ -52,7 +52,7 @@
</div>
</div>
</div>
<div class="bg-gray-200 dark:bg-gray-800 rounded-2xl shadow-lg p-8">
<div class="bg-gray-200 dark:bg-zinc-800 rounded-2xl shadow-lg p-8">
<h2 class="text-2xl font-semibold mb-4 border-b border-gray-700 pb-2">User Management & Audit</h2>
<div class="space-y-4">
<div>
+6 -6
View File
@@ -8,28 +8,28 @@
<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-gray-900 dark:text-gray-100 min-h-screen flex flex-col">
<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 max-w-md pt-20">
<h1 class="text-3xl font-bold mb-6 text-center">{{ NAME }} IPAM</h1>
<ul class="space-y-4">
{% for site, subnets in sites_subnets.items() %}
<li class="site-group bg-gray-200 dark:bg-gray-800 rounded-xl shadow-lg">
<li class="site-group bg-gray-200 dark:bg-zinc-800 rounded-xl shadow-lg">
<div class="flex flex-row items-center justify-between p-4 cursor-pointer site-header">
<h2 class="text-xl font-bold mb-0 text-gray-900 dark:text-blue-300">{{ site }}</h2>
<h2 class="text-xl font-bold mb-0 text-gray-900 dark:text-white">{{ site }}</h2>
<button type="button" class="expand-btn ml-2 flex items-center" aria-label="Expand site">
<i class="fas fa-chevron-down"></i>
</button>
</div>
<ul class="subnet-list hidden space-y-4 px-2 pb-4">
{% for subnet in subnets %}
<li class="p-4 bg-gray-300 hover:bg-gray-100 dark:bg-gray-900 hover:dark:bg-gray-700 rounded-lg shadow-md flex items-center justify-between">
<li class="p-4 bg-gray-300 hover:bg-gray-100 dark:bg-zinc-900 hover:dark:bg-zinc-700 rounded-lg shadow-md flex items-center justify-between">
<a href="/subnet/{{ subnet.id }}">
<p class="text-gray-900 dark:text-blue-400 text-lg font-medium">{{ subnet.name }}</p>
<p class="text-gray-900 dark:text-white text-lg font-medium">{{ subnet.name }}</p>
<p class="text-sm text-gray-800 dark:text-gray-400">{{ subnet.cidr }}</p>
</a>
<button type="button" class="export-csv-btn ml-2 bg-gray-200 hover:bg-gray-400 dark:bg-gray-600 dark:hover:bg-gray-500 flex items-center justify-center rounded-full w-9 h-9" title="Export as CSV" data-subnet-id="{{ subnet.id }}">
<button type="button" class="export-csv-btn ml-2 bg-gray-200 hover:bg-gray-400 dark:bg-zinc-600 dark:hover:bg-zinc-500 flex items-center justify-center rounded-full w-9 h-9" title="Export as CSV" data-subnet-id="{{ subnet.id }}">
<i class="fas fa-file-csv"></i>
</button>
</li>
+5 -5
View File
@@ -8,16 +8,16 @@
<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-gray-900 dark:text-gray-100 min-h-screen flex flex-col">
<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 max-w-md pt-20">
<div class="bg-gray-200 dark:bg-gray-800 rounded-2xl shadow-lg p-8">
<div class="bg-gray-200 dark:bg-zinc-800 rounded-2xl shadow-lg p-8">
<h1 class="text-3xl font-bold mb-6 text-center">{{ NAME }} IPAM</h1>
<form action="/login" method="POST" class="flex flex-col space-y-4">
<input type="email" name="email" placeholder="Email Address" class="p-3 rounded-lg bg-gray-200 dark:bg-gray-800 border border-gray-600" required>
<input type="password" name="password" placeholder="Password" class="p-3 rounded-lg bg-gray-200 dark:bg-gray-800 border border-gray-600" required>
<button type="submit" class="bg-gray-300 hover:bg-gray-400 dark:bg-gray-700 dark:hover:bg-gray-600 px-4 py-2 rounded-lg flex items-center gap-2 justify-center w-full">
<input type="email" name="email" placeholder="Email Address" class="p-3 rounded-lg bg-gray-200 dark:bg-zinc-800 border border-gray-600" required>
<input type="password" name="password" placeholder="Password" class="p-3 rounded-lg bg-gray-200 dark:bg-zinc-800 border border-gray-600" required>
<button type="submit" class="bg-gray-300 hover:bg-gray-400 dark:bg-zinc-700 dark:hover:bg-zinc-600 px-4 py-2 rounded-lg flex items-center gap-2 justify-center w-full">
<i class="fas fa-sign-in-alt"></i>
<span>Login</span>
</button>
+24 -24
View File
@@ -8,20 +8,20 @@
<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-gray-900 dark:text-gray-100 min-h-screen flex flex-col">
<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 max-w-2xl pt-20">
<div class="flex flex-col items-center mb-6 relative min-h-[3.5rem]">
<a href="/racks" class="hidden sm:flex absolute left-0 bg-gray-200 hover:bg-gray-400 dark:bg-gray-700 dark:hover:bg-gray-600 items-center justify-center rounded-full w-11 h-11"><i class="fas fa-arrow-left"></i></a>
<a href="/racks" 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 mb-0">{{ rack.name }}</h1>
<span class="text-base mt-1">({{ rack.height_u }}U, {{ rack.site }})</span>
<form action="/rack/{{ rack.id }}/delete" method="POST" onsubmit="return confirm('Delete this rack?');" class="hidden sm:flex absolute right-0 mr-14">
<button type="submit" class="bg-gray-200 hover:bg-gray-400 dark:bg-gray-700 dark:hover:bg-gray-600 items-center justify-center rounded-full w-11 h-11 flex" title="Delete Rack">
<button type="submit" class="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 flex" title="Delete Rack">
<i class="fas fa-times fa-lg"></i>
</button>
</form>
<button type="button" id="export-csv" class="hidden sm:flex absolute right-0 bg-gray-200 hover:bg-gray-400 dark:bg-gray-700 dark:hover:bg-gray-600 items-center justify-center rounded-full w-11 h-11 export-csv-btn" title="Export as CSV" data-rack-id="{{ rack.id }}">
<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 items-center justify-center rounded-full w-11 h-11 export-csv-btn" title="Export as CSV" data-rack-id="{{ rack.id }}">
<i class="fas fa-file-csv fa-lg"></i>
</button>
<script>
@@ -37,17 +37,17 @@
</div>
<div class="flex flex-col gap-4 mb-6 items-stretch">
<div class="flex gap-4 w-full justify-center">
<a href="?side=front" id="front-btn" class="rack-side-btn px-4 py-2 rounded-lg font-semibold bg-gray-200 hover:bg-gray-400 dark:bg-gray-700 dark:hover:bg-gray-600 {% if current_side == 'front' %}ring-2 ring-gray-400{% endif %}">Front</a>
<a href="?side=back" id="back-btn" class="rack-side-btn px-4 py-2 rounded-lg font-semibold bg-gray-200 hover:bg-gray-400 dark:bg-gray-700 dark:hover:bg-gray-600 {% if current_side == 'back' %}ring-2 ring-gray-400{% endif %}">Back</a>
<a href="?side=front" id="front-btn" class="rack-side-btn px-4 py-2 rounded-lg font-semibold bg-gray-200 hover:bg-gray-400 dark:bg-zinc-700 dark:hover:bg-zinc-600 {% if current_side == 'front' %}ring-2 ring-gray-400{% endif %}">Front</a>
<a href="?side=back" id="back-btn" class="rack-side-btn px-4 py-2 rounded-lg font-semibold bg-gray-200 hover:bg-gray-400 dark:bg-zinc-700 dark:hover:bg-zinc-600 {% if current_side == 'back' %}ring-2 ring-gray-400{% endif %}">Back</a>
</div>
<div class="flex flex-wrap gap-4 w-full justify-center">
<button id="show-add-device-form" type="button" class="flex-1 min-w-[12rem] max-w-[16rem] bg-gray-200 hover:bg-gray-400 dark:bg-gray-700 dark:hover:bg-gray-600 px-4 py-2 rounded-lg flex-shrink-0 whitespace-nowrap"> <i class="fas fa-plus"></i> Add Device</button>
<button id="show-nonnet-form" type="button" class="flex-[1.5_1.5_0%] min-w-[16rem] max-w-[28rem] bg-gray-200 hover:bg-gray-400 dark:bg-gray-700 dark:hover:bg-gray-600 px-4 py-2 rounded-lg flex-shrink-0 whitespace-nowrap"> <i class="fas fa-plus"></i> Add Non-Networked Device</button>
<button id="show-add-device-form" type="button" class="flex-1 min-w-[12rem] max-w-[16rem] bg-gray-200 hover:bg-gray-400 dark:bg-zinc-700 dark:hover:bg-zinc-600 px-4 py-2 rounded-lg flex-shrink-0 whitespace-nowrap"> <i class="fas fa-plus"></i> Add Device</button>
<button id="show-nonnet-form" type="button" class="flex-[1.5_1.5_0%] min-w-[16rem] max-w-[28rem] bg-gray-200 hover:bg-gray-400 dark:bg-zinc-700 dark:hover:bg-zinc-600 px-4 py-2 rounded-lg flex-shrink-0 whitespace-nowrap"> <i class="fas fa-plus"></i> Add Non-Networked Device</button>
</div>
</div>
<form id="add-device-form" action="/rack/{{ rack.id }}/add_device" method="POST" class="hidden mb-6 bg-gray-200 dark:bg-gray-800 rounded-lg p-4">
<form id="add-device-form" action="/rack/{{ rack.id }}/add_device" method="POST" class="hidden mb-6 bg-gray-200 dark:bg-zinc-800 rounded-lg p-4">
<div class="flex flex-col md:flex-row gap-2 mb-2">
<select name="device_id" class="border p-2 rounded bg-gray-300 text-gray-900 dark:bg-gray-900 dark:text-gray-100 border-gray-600 w-full md:flex-1" required>
<select name="device_id" class="border p-2 rounded bg-gray-300 text-gray-900 dark:bg-zinc-900 dark:text-gray-100 border-gray-600 w-full md:flex-1" required>
<option value="" disabled selected>Select Device...</option>
{% for device in site_devices %}
{% if device.device_type_id != 2 and device.device_type_id != 6 %}
@@ -55,26 +55,26 @@
{% endif %}
{% endfor %}
</select>
<input type="number" name="position_u" min="1" max="{{ rack.height_u }}" class="border p-2 rounded bg-gray-300 text-gray-900 dark:bg-gray-900 dark:text-gray-100 border-gray-600 w-full md:w-24" placeholder="U" required>
<select name="side" class="border p-2 rounded bg-gray-300 text-gray-900 dark:bg-gray-900 dark:text-gray-100 border-gray-600 w-full md:w-28" required>
<input type="number" name="position_u" min="1" max="{{ rack.height_u }}" class="border p-2 rounded bg-gray-300 text-gray-900 dark:bg-zinc-900 dark:text-gray-100 border-gray-600 w-full md:w-24" placeholder="U" required>
<select name="side" class="border p-2 rounded bg-gray-300 text-gray-900 dark:bg-zinc-900 dark:text-gray-100 border-gray-600 w-full md:w-28" required>
<option value="front">Front</option>
<option value="back">Back</option>
</select>
<button type="submit" class="w-full md:w-auto bg-gray-300 hover:bg-gray-400 dark:bg-gray-700 dark:hover:bg-gray-600 px-4 py-2 rounded-lg">Add Device</button>
<button id="hide-add-device-form" type="button" class="w-full md:w-auto bg-gray-300 hover:bg-gray-400 dark:bg-gray-700 dark:hover:bg-gray-600 px-4 py-2 rounded-lg md:ml-0.5 mt-2 md:mt-0 flex-shrink-0"><i class="fas fa-times"></i></button>
<button type="submit" class="w-full md:w-auto bg-gray-300 hover:bg-gray-400 dark:bg-zinc-700 dark:hover:bg-zinc-600 px-4 py-2 rounded-lg">Add Device</button>
<button id="hide-add-device-form" type="button" class="w-full md:w-auto bg-gray-300 hover:bg-gray-400 dark:bg-zinc-700 dark:hover:bg-zinc-600 px-4 py-2 rounded-lg md:ml-0.5 mt-2 md:mt-0 flex-shrink-0"><i class="fas fa-times"></i></button>
</div>
<div class="text-xs dark:text-gray-400">To add a multi-U device, repeat for each U position.</div>
</form>
<form id="nonnet-form" action="/rack/{{ rack.id }}/add_nonnet_device" method="POST" class="hidden flex flex-col gap-2 mt-2 mb-6 bg-gray-200 dark:bg-gray-800 rounded-lg p-4">
<form id="nonnet-form" action="/rack/{{ rack.id }}/add_nonnet_device" method="POST" class="hidden flex flex-col gap-2 mt-2 mb-6 bg-gray-200 dark:bg-zinc-800 rounded-lg p-4">
<div class="flex flex-col md:flex-row gap-2">
<input type="text" name="device_name" class="border p-2 rounded bg-gray-300 text-gray-900 dark:bg-gray-900 dark:text-gray-100 border-gray-600 w-full md:flex-1" placeholder="Device Name" required>
<input type="number" name="position_u" min="1" max="{{ rack.height_u }}" class="border p-2 rounded bg-gray-300 text-gray-900 dark:bg-gray-900 dark:text-gray-100 border-gray-600 w-full md:w-24" placeholder="U" required>
<select name="side" class="border p-2 rounded bg-gray-300 text-gray-900 dark:bg-gray-900 dark:text-gray-100 border-gray-600 w-full md:w-28" required>
<input type="text" name="device_name" class="border p-2 rounded bg-gray-300 text-gray-900 dark:bg-zinc-900 dark:text-gray-100 border-gray-600 w-full md:flex-1" placeholder="Device Name" required>
<input type="number" name="position_u" min="1" max="{{ rack.height_u }}" class="border p-2 rounded bg-gray-300 text-gray-900 dark:bg-zinc-900 dark:text-gray-100 border-gray-600 w-full md:w-24" placeholder="U" required>
<select name="side" class="border p-2 rounded bg-gray-300 text-gray-900 dark:bg-zinc-900 dark:text-gray-100 border-gray-600 w-full md:w-28" required>
<option value="front">Front</option>
<option value="back">Back</option>
</select>
<button type="submit" class="w-full md:w-auto bg-gray-300 hover:bg-gray-400 dark:bg-gray-700 dark:hover:bg-gray-600 px-4 py-2 rounded-lg">Add</button>
<button id="hide-nonnet-form" type="button" class="w-full md:w-auto bg-gray-300 hover:bg-gray-400 dark:bg-gray-700 dark:hover:bg-gray-600 px-4 py-2 rounded-lg md:ml-0.5 mt-2 md:mt-0 flex-shrink-0"><i class="fas fa-times"></i></button>
<button type="submit" class="w-full md:w-auto bg-gray-300 hover:bg-gray-400 dark:bg-zinc-700 dark:hover:bg-zinc-600 px-4 py-2 rounded-lg">Add</button>
<button id="hide-nonnet-form" type="button" class="w-full md:w-auto bg-gray-300 hover:bg-gray-400 dark:bg-zinc-700 dark:hover:bg-zinc-600 px-4 py-2 rounded-lg md:ml-0.5 mt-2 md:mt-0 flex-shrink-0"><i class="fas fa-times"></i></button>
</div>
<div class="text-xs dark:text-gray-400 mt-2">Add a non-networked device.</div>
</form>
@@ -106,8 +106,8 @@
{% if error %}
<div class="mb-4 p-3 bg-red-700 text-white rounded-lg text-center font-semibold">{{ error }}</div>
{% endif %}
<div id="rack-visual" class="bg-gray-200 dark:bg-gray-800 rounded-lg shadow-md p-4">
<h2 class="text-xl font-bold mb-2 dark:text-blue-200" id="rack-side-label">{{ current_side|capitalize }}</h2>
<div id="rack-visual" class="bg-gray-200 dark:bg-zinc-800 rounded-lg shadow-md p-4">
<h2 class="text-xl font-bold mb-2 dark:text-white" id="rack-side-label">{{ current_side|capitalize }}</h2>
<div id="rack-units">
{% for u in range(rack.height_u, 0, -1) %}
<div class="flex items-center h-10 border-b border-gray-700 hover:bg-gray-700/30 transition group">
@@ -117,9 +117,9 @@
{% for rd in rack_devices %}
{% if rd.position_u == u and rd.side == current_side %}
{% if rd.device_id %}
<a href="/device/{{ rd.device_id }}" class="dark:text-blue-400 hover:underline text-base">{{ rd.device_name }}</a>
<a href="/device/{{ rd.device_id }}" class="dark:text-white hover:underline text-base">{{ rd.device_name }}</a>
{% else %}
<span class="dark:text-blue-200 text-base">{{ rd.nonnet_device_name }}</span>
<span class="dark:text-white text-base">{{ rd.nonnet_device_name }}</span>
{% endif %}
<form action="/rack/{{ rack.id }}/remove_device" method="POST" style="display:inline" onsubmit="return confirm('Are you sure you want to remove this device from the rack?');">
<input type="hidden" name="rack_device_id" value="{{ rd.id }}">
+4 -4
View File
@@ -8,19 +8,19 @@
<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-gray-900 dark:text-gray-100 min-h-screen flex flex-col">
<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 max-w-md pt-20">
<h1 class="text-3xl font-bold mb-8 text-center">Racks</h1>
<div class="flex justify-center mb-6">
<a href="/rack/add" class="bg-gray-200 hover:bg-gray-400 dark:bg-gray-700 dark:hover:bg-gray-600 px-4 py-2 rounded-lg inline-block"><i class="fas fa-plus"></i> Add Rack</a>
<a href="/rack/add" class="bg-gray-200 hover:bg-gray-400 dark:bg-zinc-700 dark:hover:bg-zinc-600 px-4 py-2 rounded-lg inline-block"><i class="fas fa-plus"></i> Add Rack</a>
</div>
<div class="space-y-6">
{% for rack in racks %}
<a href="/rack/{{ rack.id }}" class="bg-gray-200 hover:bg-gray-100 dark:bg-gray-800 hover:dark:bg-gray-700 rounded-lg shadow-md p-4 flex items-start justify-between hover:ring-2 hover:ring-gray-400 transition group">
<a href="/rack/{{ rack.id }}" class="bg-gray-200 hover:bg-gray-100 dark:bg-zinc-800 hover:dark:bg-zinc-700 rounded-lg shadow-md p-4 flex items-start justify-between hover:ring-2 hover:ring-gray-400 transition group">
<div>
<h2 class="text-xl font-bold dark:text-blue-200 mb-1">{{ rack.name }}</h2>
<h2 class="text-xl font-bold dark:text-white mb-1">{{ rack.name }}</h2>
<div class="dark:text-gray-400">Site: {{ rack.site }} | Height: {{ rack.height_u }}U</div>
</div>
<div class="ml-6 flex-shrink-0">
+5 -5
View File
@@ -9,23 +9,23 @@
<script src="/static/js/subnet.js"></script>
<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-gray-900 dark:text-gray-100 min-h-screen flex flex-col">
<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="javascript:window.history.back()" class="hidden sm:flex absolute left-0 bg-gray-200 hover:bg-gray-400 dark:bg-gray-700 dark:hover:bg-gray-600 items-center justify-center rounded-full w-11 h-11"><i class="fas fa-arrow-left"></i></a>
<a href="javascript:window.history.back()" 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-gray-700 dark:hover:bg-gray-600 items-center justify-center rounded-full w-11 h-11 export-csv-btn" title="Export as CSV" data-subnet-id="{{ subnet.id }}">
<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 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>
<div class="flex justify-center mb-4">
<a href="/subnet/{{ subnet.id }}/dhcp" class="hidden sm:flex bg-gray-200 hover:bg-gray-400 dark:bg-gray-700 dark:hover:bg-gray-600 px-4 py-2 rounded-lg shadow items-center gap-2">
<a href="/subnet/{{ subnet.id }}/dhcp" class="hidden sm:flex 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">
<i class="fas fa-network-wired"></i> Define DHCP Pool
</a>
</div>
<button id="toggle-desc" class="sm:hidden bg-gray-300 hover:bg-gray-400 dark:bg-gray-700 dark:hover:bg-gray-600 px-4 py-2 rounded-lg mb-4 w-full">Show Descriptions</button>
<button id="toggle-desc" class="sm:hidden bg-gray-300 hover:bg-gray-400 dark:bg-zinc-700 dark:hover:bg-zinc-600 px-4 py-2 rounded-lg mb-4 w-full">Show Descriptions</button>
<form action="" method="POST">
<table class="table-auto w-full mb-6">
<thead>
+11 -11
View File
@@ -8,31 +8,31 @@
<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-gray-900 dark:text-gray-100 min-h-screen flex flex-col">
<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 max-w-6xl pt-20">
<h1 class="text-3xl font-bold mb-6 text-center">User Management</h1>
<form action="/users" method="POST" class="mb-8 bg-gray-200 dark:bg-gray-800 p-6 rounded-lg shadow-md">
<form action="/users" method="POST" class="mb-8 bg-gray-200 dark:bg-zinc-800 p-6 rounded-lg shadow-md">
<input type="hidden" name="action" value="add">
<div class="flex flex-col space-y-4 items-center w-full">
<input type="text" name="name" placeholder="Name" class="border p-3 rounded-lg bg-gray-300 text-gray-900 dark:bg-gray-900 dark:text-gray-100 border-gray-600 w-full" required>
<input type="email" name="email" placeholder="Email Address" class="border p-3 rounded-lg bg-gray-300 text-gray-900 dark:bg-gray-900 dark:text-gray-100 border-gray-600 w-full" required>
<input type="password" name="password" placeholder="Password" class="border p-3 rounded-lg bg-gray-300 text-gray-900 dark:bg-gray-900 dark:text-gray-100 border-gray-600 w-full" required>
<button type="submit" class="bg-gray-300 hover:bg-gray-400 dark:bg-gray-700 dark:hover:bg-gray-600 px-4 py-2 rounded-lg max-w-md w-full sm:w-auto">Add User</button>
<input type="text" name="name" placeholder="Name" class="border p-3 rounded-lg bg-gray-300 text-gray-900 dark:bg-zinc-900 dark:text-gray-100 border-gray-600 w-full" required>
<input type="email" name="email" placeholder="Email Address" class="border p-3 rounded-lg bg-gray-300 text-gray-900 dark:bg-zinc-900 dark:text-gray-100 border-gray-600 w-full" required>
<input type="password" name="password" placeholder="Password" class="border p-3 rounded-lg bg-gray-300 text-gray-900 dark:bg-zinc-900 dark:text-gray-100 border-gray-600 w-full" required>
<button type="submit" class="bg-gray-300 hover:bg-gray-400 dark:bg-zinc-700 dark:hover:bg-zinc-600 px-4 py-2 rounded-lg max-w-md w-full sm:w-auto">Add User</button>
</div>
</form>
<h2 class="text-xl font-bold mb-4">Existing Users</h2>
<ul class="space-y-4">
{% for user in users %}
<li class="bg-gray-200 dark:bg-gray-800 p-4 rounded-lg flex justify-between items-center">
<li class="bg-gray-200 dark:bg-zinc-800 p-4 rounded-lg flex justify-between items-center">
<form action="/users" method="POST" class="flex flex-row items-center space-x-2">
<input type="hidden" name="action" value="edit">
<input type="hidden" name="user_id" value="{{ user[0] }}">
<input type="text" name="name" value="{{ user[1] }}" class="border p-2 rounded-lg bg-gray-300 text-gray-900 dark:bg-gray-900 dark:text-gray-100 border-gray-600 w-52">
<input type="email" name="email" value="{{ user[2] }}" class="border p-2 rounded-lg bg-gray-300 text-gray-900 dark:bg-gray-900 dark:text-gray-100 border-gray-600 w-80">
<input type="password" name="password" placeholder="New Password (leave blank to keep)" class="border p-2 rounded-lg bg-gray-300 text-gray-900 dark:bg-gray-900 dark:text-gray-100 border-gray-600 w-80">
<button type="submit" class="bg-gray-300 hover:bg-gray-400 dark:bg-gray-700 dark:hover:bg-gray-600 px-3 py-1 rounded-lg">Save</button>
<input type="text" name="name" value="{{ user[1] }}" class="border p-2 rounded-lg bg-gray-300 text-gray-900 dark:bg-zinc-900 dark:text-gray-100 border-gray-600 w-52">
<input type="email" name="email" value="{{ user[2] }}" class="border p-2 rounded-lg bg-gray-300 text-gray-900 dark:bg-zinc-900 dark:text-gray-100 border-gray-600 w-80">
<input type="password" name="password" placeholder="New Password (leave blank to keep)" class="border p-2 rounded-lg bg-gray-300 text-gray-900 dark:bg-zinc-900 dark:text-gray-100 border-gray-600 w-80">
<button type="submit" class="bg-gray-300 hover:bg-gray-400 dark:bg-zinc-700 dark:hover:bg-zinc-600 px-3 py-1 rounded-lg">Save</button>
</form>
<form action="/users" method="POST" onsubmit="return confirm('Are you sure you want to delete this user?');">
<input type="hidden" name="action" value="delete">