Initial commit

This commit is contained in:
SebastianOtt 2026-05-21 20:41:03 +02:00
commit b7a3b14e06
41 changed files with 27493 additions and 0 deletions

View file

@ -0,0 +1,723 @@
---
const pageTitle = "Network Configuration";
---
<div class="flex flex-col gap-4">
<h1 class="text-2xl font-bold">{pageTitle}</h1>
<!-- Connection Status -->
<div class="flex flex-col gap-4">
<h2 class="text-xl font-bold">Connection Status</h2>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-5 gap-4">
<div class="stats shadow bg-base-200 rounded-lg border-0">
<div class="stat bg-base-200 p-3">
<div class="stat-title text-xs flex items-center gap-1">
Network Status
</div>
<div class="stat-value text-sm" id="wifi-status">Connected over WiFi</div>
<div class="stat-desc text-xs">Station Mode</div>
</div>
</div>
<div class="stats shadow bg-base-200 rounded-lg border-0">
<div class="stat bg-base-200 p-3">
<div class="stat-title text-xs flex items-center gap-1">
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="opacity-70">
<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/>
<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/>
</svg>
IP Address
</div>
<div class="stat-value text-sm" id="current-ip">192.168.1.158</div>
<div class="stat-desc text-xs">DHCP Assigned</div>
</div>
</div>
<div class="stats shadow bg-base-200 rounded-lg border-0">
<div class="stat bg-base-200 p-3">
<div class="stat-title text-xs flex items-center gap-1">
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="currentColor" class="icon icon-tabler icons-tabler-filled icon-tabler-radar opacity-70">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M12 10a2 2 0 0 1 1.678 .911l.053 .089h7.269l.117 .007a1 1 0 0 1 .883 .993c0 5.523 -4.477 10 -10 10a1 1 0 0 1 -1 -1v-7.269l-.089 -.053a2 2 0 0 1 -.906 -1.529l-.005 -.149a2 2 0 0 1 2 -2m9.428 -1.334a1 1 0 0 1 -1.884 .668a8 8 0 1 0 -10.207 10.218a1 1 0 0 1 -.666 1.886a10 10 0 1 1 12.757 -12.772m-4.628 -.266a1 1 0 0 1 -1.6 1.2a4 4 0 1 0 -5.6 5.6a1 1 0 0 1 -1.2 1.6a6 6 0 1 1 8.4 -8.4" />
</svg>
Hostname
</div>
<div class="stat-value text-sm" id="current-hostname">hydrobuddy.local</div>
<div class="stat-desc text-xs">mDNS Active</div>
</div>
</div>
<div class="stats shadow bg-base-200 rounded-lg border-0">
<div class="stat bg-base-200 p-3">
<div class="stat-title text-xs flex items-center gap-1">
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-cell-signal-5 opacity-70">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M20 20h-15.269a.731 .731 0 0 1 -.517 -1.249l14.537 -14.537a.731 .731 0 0 1 1.249 .517v15.269z" />
<path d="M16 7v13" />
<path d="M12 20v-9" />
<path d="M8 20v-5" />
</svg>
Signal Strength
</div>
<div class="stat-value text-sm" id="signal-strength">-42dBm</div>
<div class="stat-desc text-xs">
<progress class="progress progress-success w-full h-2 mt-1" value="64" max="100" id="signal-progress"></progress>
<div class="flex justify-between text-[10px] mt-1 opacity-70">
<span>-100dBm</span>
<span>-10dBm</span>
</div>
</div>
</div>
</div>
<div class="stats shadow bg-base-200 rounded-lg border-0">
<div class="stat bg-base-200 p-3">
<div class="stat-title text-xs flex items-center gap-1">
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-devices opacity-70">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M13 9a1 1 0 0 1 1 -1h6a1 1 0 0 1 1 1v10a1 1 0 0 1 -1 1h-6a1 1 0 0 1 -1 -1v-10z" />
<path d="M18 8v-3a1 1 0 0 0 -1 -1h-13a1 1 0 0 0 -1 1v12a1 1 0 0 0 1 1h9" />
<path d="M16 9h2" />
</svg>
Client Count
</div>
<div class="stat-value text-sm" id="client-count">2</div>
<div class="stat-desc text-xs">Connected Devices</div>
</div>
</div>
</div>
</div>
<!-- Network Info and WiFi Settings -->
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
<!-- Network Info -->
<div class="card bg-base-100 shadow">
<div class="card-body bg-base-200 rounded-lg">
<h2 class="card-title">Network Info</h2>
<div class="space-y-3 mt-4">
<div class="flex justify-between items-center">
<span class="text-sm font-medium">Hostname</span>
<span class="text-sm" id="info-hostname">hydrobuddy.local</span>
</div>
<div class="flex justify-between items-center">
<span class="text-sm font-medium">IP Address</span>
<span class="text-sm" id="info-ip">192.168.1.158</span>
</div>
<div class="flex justify-between items-center">
<span class="text-sm font-medium">Subnet</span>
<span class="text-sm" id="info-subnet">255.255.255.0</span>
</div>
<div class="flex justify-between items-center">
<span class="text-sm font-medium">Gateway</span>
<span class="text-sm" id="info-gateway">192.168.1.1</span>
</div>
<div class="flex justify-between items-center">
<span class="text-sm font-medium">MAC Address</span>
<span class="text-sm" id="info-mac">AA:BB:CC:DD:EE:FF</span>
</div>
<div class="flex justify-between items-center">
<span class="text-sm font-medium">Connection Method</span>
<span class="text-sm badge badge-info" id="info-method">WiFi</span>
</div>
<div class="divider my-2"></div>
<div>
<h3 class="text-sm font-medium mb-3">Connected Devices</h3>
<div class="space-y-2">
<div class="flex justify-between items-center p-2 bg-base-300 rounded-lg">
<div class="flex items-center gap-2">
<span class="text-sm">💻</span>
<div>
<div class="text-sm font-medium">laptop-office</div>
<div class="text-xs opacity-70">192.168.1.102</div>
</div>
</div>
<button class="btn btn-xs btn-error" onclick="kickDevice('laptop-office', '192.168.1.102')" title="Disconnect device">
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M18 6 6 18M6 6l12 12"/>
</svg>
</button>
</div>
<div class="flex justify-between items-center p-2 bg-base-300 rounded-lg">
<div class="flex items-center gap-2">
<span class="text-sm">📱</span>
<div>
<div class="text-sm font-medium">smartphone-dev</div>
<div class="text-xs opacity-70">192.168.1.103</div>
</div>
</div>
<button class="btn btn-xs btn-error" onclick="kickDevice('smartphone-dev', '192.168.1.103')" title="Disconnect device">
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M18 6 6 18M6 6l12 12"/>
</svg>
</button>
</div>
<div class="flex justify-between items-center p-2 bg-base-300 rounded-lg">
<div class="flex items-center gap-2">
<span class="text-sm">💻</span>
<div>
<div class="text-sm font-medium">tablet-main</div>
<div class="text-xs opacity-70">192.168.1.104</div>
</div>
</div>
<button class="btn btn-xs btn-error" onclick="kickDevice('tablet-main', '192.168.1.104')" title="Disconnect device">
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M18 6 6 18M6 6l12 12"/>
</svg>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- WiFi Settings -->
<div class="card bg-base-100 shadow">
<div class="card-body bg-base-200 rounded-lg">
<h2 class="card-title">WiFi Settings</h2>
<!-- Antenna Selection -->
<div class="form-control mb-4">
<label class="label">
<span class="label-text flex items-center gap-2">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-switch">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M15 4l4 0l0 4" />
<path d="M14.75 9.25l4.25 -5.25" />
<path d="M5 19l4 -4" />
<path d="M15 19l4 0l0 -4" />
<path d="M5 5l14 14" />
</svg>
Antenna Selection
</span>
</label>
<div class="join">
<input class="join-item btn btn-sm" type="radio" name="antenna" aria-label="📶 Internal" checked id="antenna-internal" onchange="if('vibrate' in navigator) navigator.vibrate(15)" />
<input class="join-item btn btn-sm" type="radio" name="antenna" aria-label="🔌 External" id="antenna-external" onchange="if('vibrate' in navigator) navigator.vibrate(15)" />
</div>
</div>
<!-- Transmission Power -->
<div class="form-control mb-4">
<label class="label">
<span class="label-text flex items-center gap-2">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-waves-electricity">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M3 12c.576 -.643 1.512 -1.017 2.5 -1c.988 -.017 1.924 .357 2.5 1c.576 .643 1.512 1.017 2.5 1c.988 .017 1.924 -.357 2.5 -1" />
<path d="M3 16c.576 -.643 1.512 -1.017 2.5 -1c.988 -.017 1.924 .357 2.5 1c.576 .643 1.512 1.017 2.5 1c.988 .017 1.924 -.357 2.5 -1" />
<path d="M3 8c.576 -.643 1.512 -1.017 2.5 -1c.988 -.017 1.924 .357 2.5 1c.576 .643 1.512 1.017 2.5 1c.988 .017 1.924 -.357 2.5 -1" />
<path d="M20 7l-3 5h4l-3 5" />
</svg>
Transmission Power
</span>
</label>
<div class="join w-full">
<input class="join-item btn btn-sm flex-1" type="radio" name="tx-power" aria-label="1mW" value="1" id="tx-power-1" onchange="if('vibrate' in navigator) navigator.vibrate(15)" />
<input class="join-item btn btn-sm flex-1" type="radio" name="tx-power" aria-label="10mW" value="10" id="tx-power-10" onchange="if('vibrate' in navigator) navigator.vibrate(15)" />
<input class="join-item btn btn-sm flex-1" type="radio" name="tx-power" aria-label="50mW" value="50" checked id="tx-power-50" onchange="if('vibrate' in navigator) navigator.vibrate(15)" />
<input class="join-item btn btn-sm flex-1" type="radio" name="tx-power" aria-label="100mW" value="100" id="tx-power-100" onchange="if('vibrate' in navigator) navigator.vibrate(15)" />
</div>
</div>
<!-- Mode Selection -->
<div class="form-control mb-4">
<label class="label">
<span class="label-text flex items-center gap-2">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-home-signal">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M15 22v-2" />
<path d="M18 22v-4" />
<path d="M21 22v-6" />
<path d="M19 12.494v-.494h2l-9 -9l-9 9h2v7a2 2 0 0 0 2 2h4" />
<path d="M9 21v-6a2 2 0 0 1 2 -2h2a2 2 0 0 1 2 2v.5" />
</svg>
WiFi Mode
</span>
</label>
<div class="join w-full">
<button class="btn join-item flex-1" id="station-mode-btn" onclick="if('vibrate' in navigator) navigator.vibrate(15)">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-router">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M3 13m0 2a2 2 0 0 1 2 -2h14a2 2 0 0 1 2 2v4a2 2 0 0 1 -2 2h-14a2 2 0 0 1 -2 -2z" />
<path d="M17 17l0 .01" />
<path d="M13 17l0 .01" />
<path d="M15 13l0 -2" />
<path d="M11.75 8.75a4 4 0 0 1 6.5 0" />
<path d="M8.5 6.5a8 8 0 0 1 13 0" />
</svg>
Station
</button>
<button class="btn join-item flex-1 btn-active" id="ap-mode-btn" onclick="if('vibrate' in navigator) navigator.vibrate(15)">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-wifi">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M12 18l.01 0" />
<path d="M9.172 15.172a4 4 0 0 1 5.656 0" />
<path d="M6.343 12.343a8 8 0 0 1 11.314 0" />
<path d="M3.515 9.515c4.686 -4.687 12.284 -4.687 17 0" />
</svg>
Access Point
</button>
</div>
</div>
<!-- Station Mode Settings -->
<div id="station-settings" class="space-y-4 hidden">
<div class="form-control">
<label class="label">
<span class="label-text flex items-center gap-2">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-label">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M16.52 7h-10.52a2 2 0 0 0 -2 2v6a2 2 0 0 0 2 2h10.52a1 1 0 0 0 .78 -.375l3.7 -4.625l-3.7 -4.625a1 1 0 0 0 -.78 -.375" />
</svg>
Network SSID
</span>
</label>
<div class="join w-full">
<input type="text" class="input input-bordered join-item flex-1" placeholder="Enter SSID" id="station-ssid" />
<button class="btn btn-outline join-item" id="scan-networks-btn" onclick="if('vibrate' in navigator) navigator.vibrate(15); openNetworkScanModal()">Scan</button>
</div>
</div>
<div class="form-control">
<label class="label">
<span class="label-text flex items-center gap-2">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-key">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M16.555 3.843l3.602 3.602a2.877 2.877 0 0 1 0 4.069l-2.643 2.643a2.877 2.877 0 0 1 -4.069 0l-.301 -.301l-6.558 6.558a2 2 0 0 1 -1.239 .578l-.175 .008h-1.172a1 1 0 0 1 -.993 -.883l-.007 -.117v-1.172a2 2 0 0 1 .467 -1.284l.119 -.13l.414 -.414h2v-2h2v-2l2.144 -2.144l-.301 -.301a2.877 2.877 0 0 1 0 -4.069l2.643 -2.643a2.877 2.877 0 0 1 4.069 0z" />
<path d="M15 9h.01" />
</svg>
Password
</span>
</label>
<input type="password" class="input input-bordered w-full" placeholder="WiFi Password" id="station-password" minlength="8" />
</div>
<div class="form-control">
<label class="label">
<span class="label-text flex items-center gap-2">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-shield-lock">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M12 3a12 12 0 0 0 8.5 3a12 12 0 0 1 -8.5 15a12 12 0 0 1 -8.5 -15a12 12 0 0 0 8.5 -3" />
<path d="M12 11m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
<path d="M12 12l0 2.5" />
</svg>
Security
</span>
</label>
<select class="select select-bordered w-full" id="station-security">
<option value="wpa2" selected>WPA2</option>
<option value="wpa3">WPA3</option>
<option value="open">Open</option>
</select>
</div>
</div>
<!-- Access Point Mode Settings -->
<div id="ap-settings">
<div class="form-control mb-4">
<label class="label">
<span class="label-text flex items-center gap-2">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-label">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M16.52 7h-10.52a2 2 0 0 0 -2 2v6a2 2 0 0 0 2 2h10.52a1 1 0 0 0 .78 -.375l3.7 -4.625l-3.7 -4.625a1 1 0 0 0 -.78 -.375" />
</svg>
Access Point SSID
</span>
</label>
<input type="text" class="input input-bordered w-full" value="Alpha-Driver-AP" id="ap-ssid" />
</div>
<div class="form-control mb-4">
<label class="label">
<span class="label-text flex items-center gap-2">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-key">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M16.555 3.843l3.602 3.602a2.877 2.877 0 0 1 0 4.069l-2.643 2.643a2.877 2.877 0 0 1 -4.069 0l-.301 -.301l-6.558 6.558a2 2 0 0 1 -1.239 .578l-.175 .008h-1.172a1 1 0 0 1 -.993 -.883l-.007 -.117v-1.172a2 2 0 0 1 .467 -1.284l.119 -.13l.414 -.414h2v-2h2v-2l2.144 -2.144l-.301 -.301a2.877 2.877 0 0 1 0 -4.069l2.643 -2.643a2.877 2.877 0 0 1 4.069 0z" />
<path d="M15 9h.01" />
</svg>
Password
</span>
</label>
<input type="password" class="input input-bordered w-full" placeholder="Min. 8 characters" id="ap-password" minlength="8"
pattern="(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,}"
title="Must be more than 8 characters, including number, lowercase letter, uppercase letter" />
<p class="text-xs text-base-content/70 mt-1 hidden validator-hint">
Must be more than 8 characters, including<br/>
At least one number<br/>
At least one lowercase letter<br/>
At least one uppercase letter
</p>
</div>
<div class="form-control mb-4">
<label class="label">
<span class="label-text">WiFi Channel</span>
</label>
<select class="select select-bordered w-full" id="ap-channel">
<option value="auto" selected>Auto</option>
<option value="1">Channel 1 (2.412 GHz)</option>
<option value="2">Channel 2 (2.417 GHz)</option>
<option value="3">Channel 3 (2.422 GHz)</option>
<option value="4">Channel 4 (2.427 GHz)</option>
<option value="5">Channel 5 (2.432 GHz)</option>
<option value="6">Channel 6 (2.437 GHz)</option>
<option value="7">Channel 7 (2.442 GHz)</option>
<option value="8">Channel 8 (2.447 GHz)</option>
<option value="9">Channel 9 (2.452 GHz)</option>
<option value="10">Channel 10 (2.457 GHz)</option>
<option value="11">Channel 11 (2.462 GHz)</option>
<option value="12">Channel 12 (2.467 GHz)</option>
<option value="13">Channel 13 (2.472 GHz)</option>
</select>
</div>
<div class="form-control mb-4">
<label class="label">
<span class="label-text flex items-center gap-2">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-shield-lock">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M12 3a12 12 0 0 0 8.5 3a12 12 0 0 1 -8.5 15a12 12 0 0 1 -8.5 -15a12 12 0 0 0 8.5 -3" />
<path d="M12 11m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
<path d="M12 12l0 2.5" />
</svg>
Security
</span>
</label>
<select class="select select-bordered w-full" id="ap-security">
<option value="wpa2" selected>WPA2</option>
<option value="wpa3">WPA3</option>
<option value="open">Open (Not Recommended)</option>
</select>
</div>
</div>
</div>
</div>
</div>
<!-- General Network Settings -->
<div class="card bg-base-100 shadow">
<div class="card-body bg-base-200 rounded-lg">
<h2 class="card-title">General Network Settings</h2>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mt-4">
<div>
<div class="form-control mb-4">
<label class="label">
<span class="label-text">IP Configuration</span>
</label>
<div class="join w-full">
<button class="btn join-item flex-1 btn-active" id="dhcp-btn" onclick="if('vibrate' in navigator) navigator.vibrate(15)">DHCP</button>
<button class="btn join-item flex-1" id="static-btn" onclick="if('vibrate' in navigator) navigator.vibrate(15)">Static IP</button>
</div>
</div>
<!-- Static IP Settings -->
<div id="static-ip-settings" class="space-y-2 hidden bg-base-300 rounded-lg p-3 mb-4">
<div class="form-control">
<label class="label">
<span class="label-text">IP Address</span>
</label>
<input type="text" class="input input-bordered w-full" placeholder="192.168.1.100" id="static-ip-addr" />
</div>
<div class="form-control">
<label class="label">
<span class="label-text">Subnet Mask</span>
</label>
<input type="text" class="input input-bordered w-full" value="255.255.255.0" id="subnet-mask" />
</div>
<div class="form-control">
<label class="label">
<span class="label-text">Gateway</span>
</label>
<input type="text" class="input input-bordered w-full" placeholder="192.168.1.1" id="gateway" />
</div>
<div class="form-control">
<label class="label">
<span class="label-text">DNS Server</span>
</label>
<input type="text" class="input input-bordered w-full" placeholder="8.8.8.8" id="dns-server" />
</div>
</div>
<div class="form-control mb-4">
<label class="label">
<span class="label-text">Hostname (mDNS)</span>
<span class="label-text-alt">Format: hydrobuddy.local</span>
</label>
<label class="input input-bordered flex items-center gap-2">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" class="icon icon-tabler icons-tabler-filled icon-tabler-radar opacity-70">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M12 10a2 2 0 0 1 1.678 .911l.053 .089h7.269l.117 .007a1 1 0 0 1 .883 .993c0 5.523 -4.477 10 -10 10a1 1 0 0 1 -1 -1v-7.269l-.089 -.053a2 2 0 0 1 -.906 -1.529l-.005 -.149a2 2 0 0 1 2 -2m9.428 -1.334a1 1 0 0 1 -1.884 .668a8 8 0 1 0 -10.207 10.218a1 1 0 0 1 -.666 1.886a10 10 0 1 1 12.757 -12.772m-4.628 -.266a1 1 0 0 1 -1.6 1.2a4 4 0 1 0 -5.6 5.6a1 1 0 0 1 -1.2 1.6a6 6 0 1 1 8.4 -8.4" />
</svg>
<input type="text" class="grow" value="alpha" id="hostname" pattern="[a-zA-Z0-9-]+" title="Only letters, numbers, and hyphens allowed" />
<span class="opacity-70">.local</span>
</label>
<div class="label">
<span class="label-text-alt">Only letters, numbers, and hyphens allowed</span>
</div>
</div>
</div>
<div>
<div class="form-control mb-4">
<label class="cursor-pointer label justify-start gap-4">
<span class="label-text">Enable mDNS Discovery</span>
<input type="checkbox" class="toggle toggle-success" checked id="toggle-mdns" onchange="updateToggleColor(this); if('vibrate' in navigator) navigator.vibrate(15)" />
</label>
</div>
<div class="form-control mb-4">
<label class="cursor-pointer label justify-start gap-4">
<span class="label-text flex items-center gap-2">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-refresh-dot">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M20 11a8.1 8.1 0 0 0 -15.5 -2m-.5 -4v4h4" />
<path d="M4 13a8.1 8.1 0 0 0 15.5 2m.5 4v-4h-4" />
<path d="M12 12m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
</svg>
Auto-reconnect WiFi
</span>
<input type="checkbox" class="toggle toggle-success" checked id="toggle-auto-reconnect" onchange="updateToggleColor(this); if('vibrate' in navigator) navigator.vibrate(15)" />
</label>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Network Scan Modal -->
<dialog id="network-scan-modal" class="modal">
<div class="modal-box">
<h3 class="font-bold text-lg">Available Networks</h3>
<div class="py-4">
<div class="space-y-2" id="scanned-networks">
<div class="flex justify-between items-center p-3 hover:bg-base-200 rounded cursor-pointer" onclick="selectNetwork('HomeNetwork_5G', 'WPA2')">
<div>
<div class="font-semibold">HomeNetwork_5G</div>
<div class="text-xs opacity-70">WPA2 • -35dBm</div>
</div>
<div class="text-xs">🔒</div>
</div>
<div class="flex justify-between items-center p-3 hover:bg-base-200 rounded cursor-pointer" onclick="selectNetwork('OfficeWiFi', 'WPA3')">
<div>
<div class="font-semibold">OfficeWiFi</div>
<div class="text-xs opacity-70">WPA3 • -52dBm</div>
</div>
<div class="text-xs">🔒</div>
</div>
<div class="flex justify-between items-center p-3 hover:bg-base-200 rounded cursor-pointer" onclick="selectNetwork('GuestNetwork', 'Open')">
<div>
<div class="font-semibold">GuestNetwork</div>
<div class="text-xs opacity-70">Open • -68dBm</div>
</div>
<div class="text-xs">📶</div>
</div>
<div class="flex justify-between items-center p-3 hover:bg-base-200 rounded cursor-pointer" onclick="selectNetwork('CafeWiFi', 'WPA2')">
<div>
<div class="font-semibold">CafeWiFi</div>
<div class="text-xs opacity-70">WPA2 • -71dBm</div>
</div>
<div class="text-xs">🔒</div>
</div>
</div>
</div>
<div class="modal-action">
<button class="btn btn-outline" onclick="refreshNetworkScan()">Refresh</button>
<button class="btn" onclick="closeNetworkScanModal()">Close</button>
</div>
</div>
<form method="dialog" class="modal-backdrop">
<button>close</button>
</form>
</dialog>
<script>
// Toggle color management function
function updateToggleColor(toggle: HTMLInputElement) {
if (toggle.checked) {
toggle.classList.remove('toggle-error');
toggle.classList.add('toggle-success');
} else {
toggle.classList.remove('toggle-success');
toggle.classList.add('toggle-error');
}
}
// Device kick function
function kickDevice(deviceName: string, deviceIP: string) {
if ('vibrate' in navigator) navigator.vibrate([100, 50, 100]);
// Show confirmation
if (confirm(`Disconnect ${deviceName} (${deviceIP})?`)) {
console.log(`Kicking device: ${deviceName} at ${deviceIP}`);
// In a real implementation, you would make an API call here
// For now, we'll just show a toast or update the UI
const button = event?.target as HTMLElement;
const deviceRow = button?.closest('.flex');
if (deviceRow && deviceRow instanceof HTMLElement) {
deviceRow.style.opacity = '0.5';
setTimeout(() => {
deviceRow.remove();
// Update client count
const clientCount = document.getElementById('client-count');
if (clientCount) {
const current = parseInt(clientCount.textContent || '0');
clientCount.textContent = (current - 1).toString();
}
}, 1000);
}
}
}
// Network scan modal functions
function openNetworkScanModal() {
const modal = document.getElementById('network-scan-modal') as HTMLDialogElement;
modal?.showModal();
}
function closeNetworkScanModal() {
const modal = document.getElementById('network-scan-modal') as HTMLDialogElement;
modal?.close();
}
function selectNetwork(ssid: string, security: string) {
const ssidInput = document.getElementById('station-ssid') as HTMLInputElement;
const securitySelect = document.getElementById('station-security') as HTMLSelectElement;
if (ssidInput) ssidInput.value = ssid;
if (securitySelect) {
securitySelect.value = security.toLowerCase();
}
closeNetworkScanModal();
if ('vibrate' in navigator) navigator.vibrate(15);
}
function refreshNetworkScan() {
// Simulate refreshing the network list
if ('vibrate' in navigator) navigator.vibrate(15);
console.log('Refreshing network scan...');
}
document.addEventListener('DOMContentLoaded', function() {
// Initialize toggle colors
const toggles = document.querySelectorAll('.toggle');
toggles.forEach(toggle => {
updateToggleColor(toggle as HTMLInputElement);
});
// Password validation
const passwordInputs = document.querySelectorAll('input[type="password"][pattern]');
passwordInputs.forEach(input => {
const hint = (input as HTMLInputElement).parentElement?.nextElementSibling;
input.addEventListener('focus', function() {
if (hint && hint.classList.contains('validator-hint')) {
hint.classList.remove('hidden');
}
});
input.addEventListener('blur', function() {
if (hint && hint.classList.contains('validator-hint') && (input as HTMLInputElement).validity.valid) {
hint.classList.add('hidden');
}
});
input.addEventListener('input', function() {
const isValid = (input as HTMLInputElement).validity.valid;
const parentElement = (input as HTMLInputElement).parentElement;
if (isValid) {
parentElement?.classList.remove('input-error');
parentElement?.classList.add('input-success');
if (hint) hint.classList.add('hidden');
} else {
parentElement?.classList.remove('input-success');
parentElement?.classList.add('input-error');
if (hint) hint.classList.remove('hidden');
}
});
});
// Update signal strength progress bar
function updateSignalStrength(dbm: number) {
const signalProgress = document.getElementById('signal-progress');
const signalStrength = document.getElementById('signal-strength');
if (signalProgress && signalStrength) {
// Convert dBm to percentage (-100dBm = 0%, -10dBm = 100%)
const percentage = Math.max(0, Math.min(100, ((dbm + 100) / 90) * 100));
(signalProgress as HTMLProgressElement).value = percentage;
signalStrength.textContent = dbm + 'dBm';
// Update progress bar color based on signal strength
signalProgress.className = 'progress w-full h-2 mt-1 ' +
(percentage >= 70 ? 'progress-success' :
percentage >= 40 ? 'progress-warning' : 'progress-error');
}
}
// Initialize with current signal strength
updateSignalStrength(-42);
// Mode switching
const stationBtn = document.getElementById('station-mode-btn');
const apBtn = document.getElementById('ap-mode-btn');
const stationSettings = document.getElementById('station-settings');
const apSettings = document.getElementById('ap-settings');
function switchToStation() {
stationBtn?.classList.add('btn-active');
apBtn?.classList.remove('btn-active');
stationSettings?.classList.remove('hidden');
apSettings?.classList.add('hidden');
}
function switchToAP() {
apBtn?.classList.add('btn-active');
stationBtn?.classList.remove('btn-active');
apSettings?.classList.remove('hidden');
stationSettings?.classList.add('hidden');
}
stationBtn?.addEventListener('click', switchToStation);
apBtn?.addEventListener('click', switchToAP);
// IP Configuration switching
const dhcpBtn = document.getElementById('dhcp-btn');
const staticBtn = document.getElementById('static-btn');
const staticSettings = document.getElementById('static-ip-settings');
dhcpBtn?.addEventListener('click', function() {
dhcpBtn.classList.add('btn-active');
staticBtn?.classList.remove('btn-active');
staticSettings?.classList.add('hidden');
});
staticBtn?.addEventListener('click', function() {
staticBtn.classList.add('btn-active');
dhcpBtn?.classList.remove('btn-active');
staticSettings?.classList.remove('hidden');
});
// Hostname validation
const hostnameInput = document.getElementById('hostname');
hostnameInput?.addEventListener('input', function() {
const value = (this as HTMLInputElement).value;
const valid = /^[a-zA-Z0-9-]+$/.test(value) && value.length > 0;
const parentLabel = this.closest('label');
if (!valid && value !== '') {
parentLabel?.classList.add('input-error');
} else {
parentLabel?.classList.remove('input-error');
}
});
// Initialize with Access Point mode
switchToAP();
});
</script>