Files
Numbus/web/pages/preparation.html
2026-05-25 17:11:59 +02:00

438 lines
27 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Numbus Configurator</title>
<!-- Tailwind CSS for modern styling -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- Alpine.js for lightweight reactivity -->
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
<!-- JS-YAML to convert JS objects to YAML strings -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/js-yaml/4.1.0/js-yaml.min.js"></script>
<!-- Material Design Icons -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@mdi/font@7.4.47/css/materialdesignicons.min.css">
<!-- Favicon -->
<link rel="icon" href="/media/favicon.ico" type="image/x-icon">
</head>
<body x-data="numbusPreparation()" class="p-4 bg-[#0f172a] text-slate-100 min-h-screen font-sans selection:bg-fuchsia-500/30">
<style>
[x-cloak] { display: none !important; }
</style>
<script>
function numbusPreparation() {
return {
step: 1,
discoveryLoading: false,
formData: {
1: { language: 'French', country: 'France', timeZone: 'Europe/Paris' },
2: { deviceType: '' },
3: { deploymentMode: '', gitUrl: '', gitUsername: '', gitPassword: '' },
4: { liveIp: '', livePassword: '' },
},
isStepValid() {
const currentStepData = this.formData[this.step];
if (!currentStepData) return true;
if (this.step === 3 && currentStepData.deploymentMode === 'interactive') return true;
return Object.values(currentStepData).every(value => !!value);
},
goToPrevStep() {
this.step--;
},
goToNextStep() {
this.step++;
},
async startDiscovery() {
if (this.discoveryLoading) return;
this.discoveryLoading = true;
const data = JSON.parse(JSON.stringify(this.formData));
console.log("Discovery started with:", data);
const payload = {
internationalization: {
language: data[1].language,
country: data[1].country,
time_zone: data[1].timeZone
},
device: {
type: data[2].deviceType
},
deployment: {
mode: data[3].deploymentMode,
git_url: data[3].gitUrl || '',
git_username: data[3].gitUsername || '',
git_password: data[3].gitPassword || ''
},
live_target: {
ip: data[4].liveIp,
password: data[4].livePassword
}
};
try {
const response = await fetch('/discovery', {
method: 'POST',
headers: { 'Content-Type': 'text/yaml' },
body: jsyaml.dump(payload, { quotingType: '"', forceQuotes: true })
});
if (response.ok) {
console.log("Discovery signal sent successfully.");
this.pollHardwareResults();
} else {
console.error("Server returned an error:", response.statusText);
this.discoveryLoading = false;
}
} catch (error) {
console.error("Network error during discovery:", error);
this.discoveryLoading = false;
}
},
pollHardwareResults() {
// Poll every 2 seconds for the hardware detection results file
const interval = setInterval(async () => {
try {
const response = await fetch('/config/hardware.yaml', { cache: 'no-store' });
if (response.ok) {
clearInterval(interval);
window.location.href = 'configuration.html';
}
} catch (err) {
// Errors are expected until the file is actually created
}
}, 2000);
}
}
}
</script>
<nav class="bg-[#1e293b] border border-slate-700 rounded-2xl relative">
<div class="mx-auto relative flex h-16 items-center justify-between">
<div class="absolute inset-y-0 left-0 flex items-center">
<a target="_blank" href="https://numbus.eu"><img class="w-auto h-10 pr-5 pl-8" src="/media/logo.png" aria-label="The numbus logo"></a>
<a class="font-bold text-2xl tracking-tight bg-gradient-to-r from-sky-400 to-fuchsia-500 bg-clip-text text-transparent uppercase" target="_blank" href="https://numbus.eu">NUMBUS</a>
</div>
<div class="flex flex-1 items-center justify-center">
<h1 class="sm:text-2xl sm:pr-20 lg:pr-0 text-xl text-slate-200 font-bold flex items-center">Step 1 - Preparation</h1>
</div>
<div class="absolute inset-y-0 right-0 flex items-center my-auto">
<button class="h-auto mdi mdi-menu text-slate-100 text-2xl sm:hidden pr-8" aria-label="Menu with links"></button>
<button class="h-auto mdi mdi-brightness-2 text-slate-100 text-2xl hidden sm:block sm:text-3xl pr-5" aria-label="Change theme"></button>
<a target="_blank" href="https://gittea.dev/numbus" class="h-auto mdi mdi-source-repository text-slate-100 text-2xl hidden sm:block sm:text-3xl pr-5" aria-label="See the source code on Gitea"></a>
<a target="_blank" href="https://gittea.dev/numbus" class="h-auto mdi mdi-text-box-search text-slate-100 text-2xl hidden sm:block sm:text-3xl pr-8" aria-label="See the full documentation"></a>
</div>
</div>
</nav>
<div class="pt-10 w-full"></div>
<!-- Main content -->
<div class="bg-[#1e293b] border border-slate-700 rounded-3xl h-[calc(100vh-10rem)] max-w-[60vw] mx-auto relative">
<!-- Step 1: Language & Region -->
<div x-show="step === 1" x-cloak class="pl-3 pr-3">
<h2 class="p-5 pt-6 text-4xl font-bold text-sky-400">Language</h2>
<div class="p-0.5 bg-slate-700 rounded-3xl w-[97%] mx-auto"></div>
<p class="p-5 text-xl text-slate-200">Set your regional preferences to ensure <b>correct</b> time and language display.</p>
<div class="pl-5 pr-5 pt-10 pb-10 grid grid-cols-2 gap-4">
<div class="space-y-2 relative" x-data="{ infoBubbleOpen: false }">
<div class="flex items-center gap-2">
<label class="text-lg font-semibold text-slate-300">System Language</label>
<button class="relative" @mouseenter="infoBubbleOpen = true" @mouseleave="infoBubbleOpen = false">
<i class="mdi mdi-information text-lg text-fuchsia-600 cursor-help transition-colors duration-300 hover:text-fuchsia-500"></i>
<div
x-show="infoBubbleOpen"
x-transition:enter="transition ease-out duration-150"
x-transition:enter-start="opacity-0 scale-90"
x-transition:enter-end="opacity-100 scale-100"
x-transition:leave="transition ease-in duration-300"
x-transition:leave-start="opacity-100 scale-100"
x-transition:leave-end="opacity-0 scale-90"
x-cloak class="absolute left-full top-1/2 -translate-y-1/2 ml-3 w-80 p-5 bg-slate-800 rounded-xl border border-slate-600 shadow-2xl shadow-slate-900 z-50">
<p class="text-slate-200 text-lg italic">Select the primary language for the operating system and management interfaces.</p>
</div>
</button>
</div>
<select x-model="formData[1].language" class="w-full bg-slate-900 border border-slate-700 rounded-xl p-4 focus:ring-2 focus:ring-fuchsia-500 outline-none transition-all appearance-none cursor-pointer">
<option value="FR">French</option>
<option value="EN">English</option>
<option value="DE">German</option>
<option value="IT">Italian</option>
<option value="ES">Spanish</option>
</select>
</div>
<div class="space-y-2 relative" x-data="{ infoBubbleOpen: false }">
<div class="flex items-center gap-2">
<label class="text-lg font-semibold text-slate-300">Country</label>
<button class="relative" @mouseenter="infoBubbleOpen = true" @mouseleave="infoBubbleOpen = false">
<i class="mdi mdi-information text-lg text-fuchsia-600 cursor-help transition-colors duration-300 hover:text-fuchsia-500"></i>
<div
x-show="infoBubbleOpen"
x-transition:enter="transition ease-out duration-150"
x-transition:enter-start="opacity-0 scale-90"
x-transition:enter-end="opacity-100 scale-100"
x-transition:leave="transition ease-in duration-300"
x-transition:leave-start="opacity-100 scale-100"
x-transition:leave-end="opacity-0 scale-90"
x-cloak class="absolute left-full top-1/2 -translate-y-1/2 ml-3 w-80 p-5 bg-slate-800 rounded-xl border border-slate-600 shadow-2xl shadow-slate-900 z-50">
<p class="text-slate-200 text-lg italic">Select the country where you are located to help define the device's locale.</p>
</div>
</button>
</div>
<select x-model="formData[1].country" class="w-full bg-slate-900 border border-slate-700 rounded-xl p-4 focus:ring-2 focus:ring-fuchsia-500 outline-none appearance-none cursor-pointer">
<option value="fr_FR">France</option>
<option value="de_DE">Germany</option>
<option value="it_IT">Italy</option>
<option value="en_GB">United Kingdom</option>
<option value="en_US">United States</option>
</select>
</div>
<div class="space-y-2 relative" x-data="{ infoBubbleOpen: false }">
<div class="flex items-center gap-2">
<label class="text-lg font-semibold text-slate-300">Time zone</label>
<button class="relative" @mouseenter="infoBubbleOpen = true" @mouseleave="infoBubbleOpen = false">
<i class="mdi mdi-information text-lg text-fuchsia-600 cursor-help transition-colors duration-300 hover:text-fuchsia-500"></i>
<div
x-show="infoBubbleOpen"
x-transition:enter="transition ease-out duration-150"
x-transition:enter-start="opacity-0 scale-90"
x-transition:enter-end="opacity-100 scale-100"
x-transition:leave="transition ease-in duration-300"
x-transition:leave-start="opacity-100 scale-100"
x-transition:leave-end="opacity-0 scale-90"
x-cloak class="absolute left-full top-1/2 -translate-y-1/2 ml-3 w-80 p-5 bg-slate-800 rounded-xl border border-slate-600 shadow-2xl shadow-slate-900 z-50">
<p class="text-slate-200 text-lg italic">Select the time zone in which the device is located. <br> Don't know your time zone ? Find it at <a target="_blank" href="https://en.wikipedia.org/wiki/List_of_UTC_offsets" class="text-sky-400 underline font-semibold">Wikipedia</a>.</p>
</div>
</button>
</div>
<select x-model="formData[1].timeZone" class="w-full bg-slate-900 border border-slate-700 rounded-xl p-4 focus:ring-2 focus:ring-fuchsia-500 outline-none appearance-none cursor-pointer">
<option value="Europe/Paris">Europe/Paris</option>
<option value="Europe/Berlin">Europe/Berlin</option>
<option value="Europe/London">Europe/London</option>
<option value="America/New_York">America/New_York</option>
<option value="America/Los_Angeles">America/Los_Angeles</option>
<option value="UTC">UTC</option>
</select>
</div>
</div>
</div>
<!-- Step 1: Language & Region -->
<!-- Device Type -->
<div x-show="step === 2" x-cloak class="pl-3 pr-3">
<h2 class="p-5 pt-6 text-4xl font-bold text-sky-400">Device Type</h2>
<div class="p-0.5 bg-slate-700 rounded-3xl w-[97%] mx-auto"></div>
<p class="p-5 text-xl text-slate-300">Select the <b>device type</b> for your new Numbus machine that matches <b>your needs</b>.</p>
<div class="pl-5 pr-5 pt-10 pb-10 grid grid-cols-2 gap-4">
<button @click="formData[2].deviceType = 'server'" :class="formData[2].deviceType === 'server' ? 'bg-fuchsia-600/20 border-fuchsia-500' : 'bg-slate-900 border-slate-700'" class="flex items-center gap-4 p-6 border rounded-2xl transition-all text-left hover:bg-fuchsia-600/20 hover:border-fuchsia-500">
<img class="w-12 h-12 flex-shrink-0" src="/media/light/numbus-server-light.svg" alt="Numbus Server icon">
<div x-data="{ infoBubbleOpen: false }">
<div class="items-center gap-2 flex">
<span class="font-bold text-2xl">Numbus Server</span>
<span class="relative" @mouseenter="infoBubbleOpen = true" @mouseleave="infoBubbleOpen = false">
<i class="mdi mdi-information text-lg text-fuchsia-600 cursor-help transition-colors duration-300 hover:text-fuchsia-500"></i>
<div
x-show="infoBubbleOpen"
x-transition:enter="transition ease-out duration-150"
x-transition:enter-start="opacity-0 scale-90"
x-transition:enter-end="opacity-100 scale-100"
x-transition:leave="transition ease-in duration-300"
x-transition:leave-start="opacity-100 scale-100"
x-transition:leave-end="opacity-0 scale-90"
x-cloak class="absolute left-full top-1/2 -translate-y-1/2 ml-3 w-80 p-5 bg-slate-800 rounded-xl border border-slate-600 shadow-2xl z-50 text-sm leading-relaxed">
<p class="text-slate-300 text-lg italic">A versatile home cloud solution for hosting containers, media, and automated services.</p>
</div>
</span>
</div>
<p class="text-sm transition-colors">Your own Cloud at Home.</p>
</div>
</button>
<button @click="formData[2].deviceType = 'backup-server'" :class="formData[2].deviceType === 'backup-server' ? 'bg-fuchsia-600/20 border-fuchsia-500' : 'bg-slate-900 border-slate-700'" class="flex items-center gap-4 p-6 border rounded-2xl transition-all text-left hover:bg-fuchsia-600/20 hover:border-fuchsia-500">
<img class="w-12 h-12 flex-shrink-0" src="/media/light/numbus-backup-server-light.svg" alt="Numbus Backup Server icon">
<div x-data="{ infoBubbleOpen: false }">
<div class="items-center flex gap-2">
<span class="font-bold text-2xl mb-1">Numbus Backup Server</span>
<span class="relative" @mouseenter="infoBubbleOpen = true" @mouseleave="infoBubbleOpen = false">
<i class="mdi mdi-information text-lg text-fuchsia-600 cursor-help transition-colors duration-300 hover:text-fuchsia-500"></i>
<div
x-show="infoBubbleOpen"
x-transition:enter="transition ease-out duration-150"
x-transition:enter-start="opacity-0 scale-90"
x-transition:enter-end="opacity-100 scale-100"
x-transition:leave="transition ease-in duration-300"
x-transition:leave-start="opacity-100 scale-100"
x-transition:leave-end="opacity-0 scale-90"
x-cloak class="absolute left-full top-1/2 -translate-y-1/2 ml-3 w-80 p-5 bg-slate-800 rounded-xl border border-slate-600 shadow-2xl z-50 text-sm leading-relaxed">
<p class="text-slate-300 text-lg italic">An all-in-one backup solution for all your Numbus devices, with monitoring tools.</p>
</div>
</span>
</div>
<p class="text-sm transition-colors">Backup all Numbus devices and monitor your servers.</p>
</div>
</button>
<button @click="formData[2].deviceType = 'computer'" :class="formData[2].deviceType === 'computer' ? 'bg-fuchsia-600/20 border-fuchsia-500' : 'bg-slate-900 border-slate-700'" class="flex items-center gap-4 p-6 border rounded-2xl transition-all text-left hover:bg-fuchsia-600/20 hover:border-fuchsia-500">
<img class="w-12 h-12 flex-shrink-0" src="/media/light/numbus-computer-light.svg" alt="Numbus Computer icon">
<div x-data="{ infoBubbleOpen: false }">
<div class="items-center flex gap-2">
<span class="font-bold text-2xl mb-1">Numbus Computer</span>
<span class="relative" @mouseenter="infoBubbleOpen = true" @mouseleave="infoBubbleOpen = false">
<i class="mdi mdi-information text-lg text-fuchsia-600 cursor-help transition-colors duration-300 hover:text-fuchsia-500"></i>
<div
x-show="infoBubbleOpen"
x-transition:enter="transition ease-out duration-150"
x-transition:enter-start="opacity-0 scale-90"
x-transition:enter-end="opacity-100 scale-100"
x-transition:leave="transition ease-in duration-300"
x-transition:leave-start="opacity-100 scale-100"
x-transition:leave-end="opacity-0 scale-90"
x-cloak class="absolute left-full top-1/2 -translate-y-1/2 ml-3 w-80 p-5 bg-slate-800 rounded-xl border border-slate-600 shadow-2xl z-50 text-sm leading-relaxed">
<p class="text-slate-300 text-lg italic">A polished workstation powered by leading open-source software.</p>
</div>
</span>
</div>
<p class="text-sm transition-colors">A workstation powered by leading open-source software.</p>
</div>
</button>
<button @click="formData[2].deviceType = 'tv'" :class="formData[2].deviceType === 'tv' ? 'bg-fuchsia-600/20 border-fuchsia-500' : 'bg-slate-900 border-slate-700'" class="flex items-center gap-4 p-6 border rounded-2xl transition-all text-left hover:bg-fuchsia-600/20 hover:border-fuchsia-500">
<img class="w-12 h-12 flex-shrink-0" src="/media/light/numbus-tv-light.svg" alt="Numbus TV icon">
<div x-data="{ infoBubbleOpen: false }">
<div class="items-center flex gap-2">
<span class="font-bold text-2xl mb-1">Numbus TV</span>
<span class="relative" @mouseenter="infoBubbleOpen = true" @mouseleave="infoBubbleOpen = false">
<i class="mdi mdi-information text-lg text-fuchsia-600 cursor-help transition-colors duration-300 hover:text-fuchsia-500"></i>
<div
x-show="infoBubbleOpen"
x-transition:enter="transition ease-out duration-150"
x-transition:enter-start="opacity-0 scale-90"
x-transition:enter-end="opacity-100 scale-100"
x-transition:leave="transition ease-in duration-300"
x-transition:leave-start="opacity-100 scale-100"
x-transition:leave-end="opacity-0 scale-90"
x-cloak class="absolute left-full top-1/2 -translate-y-1/2 ml-3 w-80 p-5 bg-slate-800 rounded-xl border border-slate-600 shadow-2xl z-50 text-sm leading-relaxed">
<p class="text-slate-300 text-lg italic">A computer -and all its accompanying advantages- but with a slick, familiar TV interface.</p>
</div>
</span>
</div>
<p class="text-sm transition-colors">Your TV, your way. No spying on you.</p>
</div>
</button>
</div>
</div>
<!-- Device Type -->
<!-- Deployment Mode -->
<div x-show="step === 3" x-cloak class="pl-3 pr-3">
<h2 class="p-5 pt-6 text-4xl font-bold text-sky-400">Deployment Mode</h2>
<div class="p-0.5 bg-slate-700 rounded-3xl w-[97%] mx-auto"></div>
<p class="p-5 text-xl text-slate-300">Select your <b>preferred</b> deployment mode. Non-interactive <b>requires</b> a ready-to-go configuration hosted on a <b>git platform</b>.</p>
<div class="pl-5 pr-5 pt-10 grid grid-cols-2 gap-4">
<button @click="formData[3].deploymentMode = 'interactive'" :class="formData[3].deploymentMode === 'interactive' ? 'bg-fuchsia-600/20 border-fuchsia-500' : 'bg-slate-900 border-slate-700'" class="flex items-center gap-4 p-6 border rounded-2xl transition-all text-left hover:bg-fuchsia-600/20 hover:border-fuchsia-500">
<i class="mdi mdi-gesture-tap text-5xl flex-shrink-0"></i>
<div x-data="{ infoBubbleOpen: false }">
<div class="items-center flex gap-2">
<span class="font-bold text-2xl mb-1">Interactive</span>
<span class="relative" @mouseenter="infoBubbleOpen = true" @mouseleave="infoBubbleOpen = false">
<i class="mdi mdi-information text-lg text-fuchsia-600 cursor-help transition-colors duration-300 hover:text-fuchsia-500"></i>
<div
x-show="infoBubbleOpen"
x-transition:enter="transition ease-out duration-150"
x-transition:enter-start="opacity-0 scale-90"
x-transition:enter-end="opacity-100 scale-100"
x-transition:leave="transition ease-in duration-300"
x-transition:leave-start="opacity-100 scale-100"
x-transition:leave-end="opacity-0 scale-90"
x-cloak class="absolute left-full top-1/2 -translate-y-1/2 ml-3 w-80 p-5 bg-slate-800 rounded-xl border border-slate-600 shadow-2xl z-50 text-sm leading-relaxed">
<p class="text-slate-300 text-lg italic">If this is your first time setting up the Numbus device you chose, follow this option.</p>
</div>
</span>
</div>
<p class="text-sm transition-colors">We will guide you through the setup process.</p>
</div>
</button>
<button @click="formData[3].deploymentMode = 'non-interactive'" :class="formData[3].deploymentMode === 'non-interactive' ? 'bg-fuchsia-600/20 border-fuchsia-500' : 'bg-slate-900 border-slate-700'" class="flex items-center gap-4 p-6 border rounded-2xl transition-all text-left hover:bg-fuchsia-600/20 hover:border-fuchsia-500">
<i class="mdi mdi-cog-clockwise text-5xl flex-shrink-0"></i>
<div x-data="{ infoBubbleOpen: false }">
<div class="items-center flex gap-2">
<span class="font-bold text-2xl mb-1">Non-interactive</span>
<span class="relative" @mouseenter="infoBubbleOpen = true" @mouseleave="infoBubbleOpen = false">
<i class="mdi mdi-information text-lg text-fuchsia-600 cursor-help transition-colors duration-300 hover:text-fuchsia-500"></i>
<div
x-show="infoBubbleOpen"
x-transition:enter="transition ease-out duration-150"
x-transition:enter-start="opacity-0 scale-90"
x-transition:enter-end="opacity-100 scale-100"
x-transition:leave="transition ease-in duration-300"
x-transition:leave-start="opacity-100 scale-100"
x-transition:leave-end="opacity-0 scale-90"
x-cloak class="absolute left-full top-1/2 -translate-y-1/2 ml-3 w-80 p-5 bg-slate-800 rounded-xl border border-slate-600 shadow-2xl z-50 text-sm leading-relaxed">
<p class="text-slate-300 text-lg italic">This option is used for mass devices deployments. It requires a first run in interactive mode.</p>
</div>
</span>
</div>
<p class="text-sm transition-colors">You already have a ready-to-go configuration.</p>
</div>
</button>
</div>
</div>
<!-- Deployment Mode -->
<!-- Live Setup -->
<div x-show="step === 4" x-cloak class="pl-3 pr-3">
<h2 class="p-5 pt-6 text-4xl font-bold text-sky-400">Live Setup</h2>
<div class="p-0.5 bg-slate-700 rounded-3xl w-[97%] mx-auto"></div>
<p class="p-5 pb-10 text-xl text-slate-300">Provide the <b>necessary information</b> to connect to the device. It needs to be in a <b>NixOS live environment</b>.</p>
<div class="flex flex-col items-center justify-center">
<div class="p-6 space-y-2">
<span class="text-base font-bold text-center text-slate-300 uppercase">Live Target IP Address</span>
<input x-model="formData[4].liveIp" class="w-full bg-slate-900 border border-slate-700 rounded-xl p-4 focus:ring-2 focus:ring-fuchsia-500 outline-none placeholder:text-slate-500/40" type="text" placeholder="192.168.1.100">
</div>
<div class="p-6 space-y-2">
<span class="text-base font-bold text-center text-slate-300 uppercase">Temporary Password</span>
<input x-model="formData[4].livePassword" class="w-full bg-slate-900 border border-slate-700 rounded-xl p-4 focus:ring-2 focus:ring-fuchsia-500 outline-none placeholder:text-slate-500/40" type="password" placeholder="••••••••">
</div>
</div>
</div>
<!-- Live Setup -->
<!-- Footer -->
<div class="bg-[#1e293b] border-t bottom-0 w-full rounded-3xl absolute border-slate-700 p-6 flex items-center justify-between">
<div x-show="step === 1" class="px-8 py-3"></div>
<button @click="goToPrevStep()" x-show="step > 1" x-cloak class="px-8 py-3 text-slate-400 font-bold rounded-xl hover:text-white transition-colors duration-200">Back</button>
<button @click="goToNextStep()"
x-show="step != 4"
:disabled="!isStepValid()"
class="px-10 py-3 text-white font-bold rounded-xl scale-100 transition duration-200 ease-in bg-gradient-to-r from-fuchsia-500 via-fuchsia-600 to-fuchsia-700 hover:scale-105 hover:bg-gradient-to-br shadow-lg shadow-fuchsia-500/30 dark:shadow-lg dark:shadow-fuchsia-800/80 disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100">Continue</button>
<button
@click="startDiscovery()"
x-show="step === 4"
x-cloak
:disabled="!isStepValid() || discoveryLoading"
class="px-10 py-3 text-white font-bold rounded-xl flex items-center gap-3 scale-100 transition duration-200 ease-in bg-gradient-to-r from-fuchsia-500 via-fuchsia-600 to-fuchsia-700 hover:scale-105 hover:bg-gradient-to-br shadow-lg shadow-fuchsia-500/30 dark:shadow-lg dark:shadow-fuchsia-800/80 disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100">
<span x-text="discoveryLoading ? 'Discovering...' : 'Start Discovery'"></span>
<svg x-show="discoveryLoading" aria-hidden="true" class="w-5 h-5 text-white animate-spin" viewBox="0 0 100 101" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z" fill="currentColor"/>
<path d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z" fill="currentFill"/>
</svg>
</button>
</div>
<!-- Footer -->
</div>
</body>
</html>