Files
hNetplan/index.html
2026-02-18 11:38:06 +03:00

668 lines
49 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="tr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Netplan Yapılandırma Oluşturucu</title>
<!-- Tailwind CSS -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- Vue.js -->
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<!-- js-yaml -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/js-yaml/4.1.0/js-yaml.min.js"></script>
<!-- Custom CSS -->
<link rel="stylesheet" href="style.css">
<!-- Google Fonts -->
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<script>
tailwind.config = {
darkMode: 'class',
theme: {
extend: {
fontFamily: {
sans: ['Inter', 'sans-serif'],
},
colors: {
primary: {
50: '#f0fdf4',
100: '#dcfce7',
200: '#bbf7d0',
300: '#86efac',
400: '#4ade80',
500: '#22c55e',
600: '#16a34a',
700: '#15803d',
800: '#166534',
900: '#14532d',
}
}
}
}
}
</script>
</head>
<body class="bg-gray-50 dark:bg-gray-900 text-gray-800 dark:text-gray-200 transition-colors duration-300 font-sans">
<div id="app" class="min-h-screen flex flex-col">
<!-- Header -->
<header class="bg-white dark:bg-gray-800 shadow-sm sticky top-0 z-50">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4 flex justify-between items-center">
<div class="flex items-center space-x-3">
<div class="bg-primary-600 text-white p-2 rounded-lg shadow-lg">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />
</svg>
</div>
<h1
class="text-2xl font-bold bg-clip-text text-transparent bg-gradient-to-r from-primary-600 to-primary-400">
Netplan Config Generator
</h1>
</div>
<div class="flex items-center space-x-4">
<button @click="toggleTheme"
class="p-2 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors">
<span v-if="isDark">🌞</span>
<span v-else>🌙</span>
</button>
<button @click="toggleLanguage"
class="flex items-center space-x-1 px-3 py-1.5 rounded-md text-sm font-medium bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors">
<span>{{ currentLang === 'tr' ? '🇹🇷 TR' : '🇬🇧 EN' }}</span>
</button>
</div>
</div>
</header>
<!-- Main Content -->
<main class="flex-grow container mx-auto px-4 sm:px-6 lg:px-8 py-8">
<div class="grid grid-cols-1 lg:grid-cols-12 gap-8">
<!-- Configuration Form -->
<div class="lg:col-span-7 space-y-6">
<!-- Global Settings -->
<div
class="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-100 dark:border-gray-700 overflow-hidden">
<div
class="px-6 py-4 border-b border-gray-100 dark:border-gray-700 bg-gray-50 dark:bg-gray-800/50 flex justify-between items-center">
<h2 class="text-lg font-semibold text-gray-900 dark:text-white flex items-center">
<span class="mr-2">🌐</span> {{ t('globalSettings') }}
</h2>
</div>
<div class="p-6 grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">{{
t('renderer') }}</label>
<select v-model="config.renderer"
class="w-full rounded-lg border-gray-300 dark:border-gray-600 dark:bg-gray-700 focus:border-primary-500 focus:ring-primary-500 shadow-sm transition-colors">
<option value="networkd">networkd (Server Default)</option>
<option value="NetworkManager">NetworkManager (Desktop Default)</option>
</select>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">{{
t('version') }}</label>
<input type="number" v-model.number="config.version"
class="w-full rounded-lg border-gray-300 dark:border-gray-600 dark:bg-gray-700 focus:border-primary-500 focus:ring-primary-500 shadow-sm"
readonly>
</div>
</div>
</div>
<!-- Ethernets -->
<div
class="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-100 dark:border-gray-700 overflow-hidden">
<div
class="px-6 py-4 border-b border-gray-100 dark:border-gray-700 bg-gray-50 dark:bg-gray-800/50 flex justify-between items-center">
<h2 class="text-lg font-semibold text-gray-900 dark:text-white flex items-center">
<span class="mr-2">🔌</span> {{ t('ethernets') }}
</h2>
<button @click="addEthernet"
class="inline-flex items-center px-3 py-1.5 border border-transparent text-xs font-medium rounded-md shadow-sm text-white bg-primary-600 hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500 transition-colors">
<svg class="-ml-0.5 mr-2 h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"
fill="currentColor">
<path fill-rule="evenodd"
d="M10 3a1 1 0 011 1v5h5a1 1 0 110 2h-5v5a1 1 0 11-2 0v-5H4a1 1 0 110-2h5V4a1 1 0 011-1z"
clip-rule="evenodd" />
</svg>
{{ t('addInterface') }}
</button>
</div>
<div v-if="config.ethernets.length === 0"
class="p-8 text-center text-gray-500 dark:text-gray-400">
{{ t('noInterfaces') }}
</div>
<div v-else class="divide-y divide-gray-100 dark:divide-gray-700">
<div v-for="(eth, index) in config.ethernets" :key="index"
class="p-6 hover:bg-gray-50 dark:hover:bg-gray-700/30 transition-colors relative group">
<button @click="removeEthernet(index)"
class="absolute top-4 right-4 text-gray-400 hover:text-red-500 opacity-0 group-hover:opacity-100 transition-all p-1">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20"
fill="currentColor">
<path fill-rule="evenodd"
d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z"
clip-rule="evenodd" />
</svg>
</button>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
<div>
<label
class="block text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-1">{{
t('interfaceName') }}</label>
<input type="text" v-model="eth.name" placeholder="e.g. eth0"
class="block w-full rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm">
</div>
<div class="flex items-center pt-6">
<input type="checkbox" v-model="eth.dhcp4" :id="'dhcp4-'+index"
class="h-4 w-4 text-primary-600 focus:ring-primary-500 border-gray-300 rounded">
<label :for="'dhcp4-'+index"
class="ml-2 block text-sm text-gray-900 dark:text-gray-300">DHCPv4</label>
</div>
</div>
<div v-if="!eth.dhcp4" class="space-y-4 animate-fade-in-down">
<div>
<label
class="block text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-1">{{
t('ipAddresses') }}</label>
<div class="flex space-x-2">
<input type="text" v-model="eth.addressesInput"
placeholder="192.168.1.10/24, 10.0.0.5/24"
class="block w-full rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm">
</div>
<p class="mt-1 text-xs text-gray-400">{{ t('commaSeparated') }}</p>
</div>
<div>
<label
class="block text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-1">{{
t('gateway4') }}</label>
<input type="text" v-model="eth.gateway4" placeholder="192.168.1.1"
class="block w-full rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm">
</div>
<div>
<label
class="block text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-1">{{
t('nameservers') }}</label>
<input type="text" v-model="eth.nameserversInput" placeholder="8.8.8.8, 1.1.1.1"
class="block w-full rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm">
</div>
</div>
<div class="mt-4 pt-4 border-t border-gray-100 dark:border-gray-700">
<button @click="eth.showAdvanced = !eth.showAdvanced"
class="text-xs text-primary-600 hover:text-primary-700 dark:text-primary-400 dark:hover:text-primary-300 font-medium flex items-center">
<span class="mr-1">{{ eth.showAdvanced ? '' : '+' }}</span> {{
t('advancedSettings') }}
</button>
<div v-if="eth.showAdvanced"
class="mt-4 grid grid-cols-1 md:grid-cols-2 gap-4 animate-fade-in-down">
<div>
<label
class="block text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-1">MTU</label>
<input type="number" v-model.number="eth.mtu" placeholder="1500"
class="block w-full rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm">
</div>
<div>
<label
class="block text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-1">MAC
Address (Optional)</label>
<input type="text" v-model="eth.macaddress" placeholder="XX:XX:XX:XX:XX:XX"
class="block w-full rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm">
</div>
<div class="col-span-1 md:col-span-2">
<label
class="block text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-1">{{
t('routes') }}</label>
<div v-for="(route, rIndex) in eth.routes" :key="rIndex"
class="flex space-x-2 mb-2">
<input type="text" v-model="route.to" placeholder="To: 10.0.0.0/24"
class="flex-1 rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm">
<input type="text" v-model="route.via" placeholder="Via: 192.168.1.1"
class="flex-1 rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm">
<button @click="eth.routes.splice(rIndex, 1)"
class="text-red-500 hover:text-red-700">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5"
viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd"
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
clip-rule="evenodd" />
</svg>
</button>
</div>
<button @click="eth.routes.push({to: '', via: ''})"
class="text-xs text-primary-600 hover:text-primary-700 font-medium flex items-center mt-1">
<svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3 mr-1"
viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd"
d="M10 3a1 1 0 011 1v5h5a1 1 0 110 2h-5v5a1 1 0 11-2 0v-5H4a1 1 0 110-2h5V4a1 1 0 011-1z"
clip-rule="evenodd" />
</svg>
Add Route
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Wi-Fi Settings -->
<div
class="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-100 dark:border-gray-700 overflow-hidden">
<div
class="px-6 py-4 border-b border-gray-100 dark:border-gray-700 bg-gray-50 dark:bg-gray-800/50 flex justify-between items-center">
<h2 class="text-lg font-semibold text-gray-900 dark:text-white flex items-center">
<span class="mr-2">📶</span> {{ t('wifis') }}
</h2>
<button @click="addWifi"
class="inline-flex items-center px-3 py-1.5 border border-transparent text-xs font-medium rounded-md shadow-sm text-white bg-primary-600 hover:bg-primary-700 focus:outline-none transition-colors">
<svg class="-ml-0.5 mr-2 h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"
fill="currentColor">
<path fill-rule="evenodd"
d="M10 3a1 1 0 011 1v5h5a1 1 0 110 2h-5v5a1 1 0 11-2 0v-5H4a1 1 0 110-2h5V4a1 1 0 011-1z"
clip-rule="evenodd" />
</svg>
{{ t('addWifi') }}
</button>
</div>
<div v-if="config.wifis.length === 0" class="p-8 text-center text-gray-500 dark:text-gray-400">
{{ t('noWifis') }}
</div>
<div v-else class="divide-y divide-gray-100 dark:divide-gray-700">
<div v-for="(wifi, index) in config.wifis" :key="index"
class="p-6 hover:bg-gray-50 dark:hover:bg-gray-700/30 transition-colors relative group">
<button @click="removeWifi(index)"
class="absolute top-4 right-4 text-gray-400 hover:text-red-500 opacity-0 group-hover:opacity-100 transition-all p-1">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20"
fill="currentColor">
<path fill-rule="evenodd"
d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z"
clip-rule="evenodd" />
</svg>
</button>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
<div>
<label
class="block text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-1">{{
t('interfaceName') }}</label>
<input type="text" v-model="wifi.name" placeholder="wlan0"
class="block w-full rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm">
</div>
<div>
<label
class="block text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-1">SSID</label>
<input type="text" v-model="wifi.accessPoints[0].ssid"
placeholder="Network Name"
class="block w-full rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm">
</div>
<div>
<label
class="block text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-1">Password</label>
<input type="password" v-model="wifi.accessPoints[0].password" placeholder="Key"
class="block w-full rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm">
</div>
<div class="flex items-center pt-6">
<input type="checkbox" v-model="wifi.dhcp4" :id="'wifi-dhcp4-'+index"
class="h-4 w-4 text-primary-600 focus:ring-primary-500 border-gray-300 rounded">
<label :for="'wifi-dhcp4-'+index"
class="ml-2 block text-sm text-gray-900 dark:text-gray-300">DHCPv4</label>
</div>
</div>
</div>
</div>
</div>
<!-- Bridges -->
<div
class="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-100 dark:border-gray-700 overflow-hidden">
<div
class="px-6 py-4 border-b border-gray-100 dark:border-gray-700 bg-gray-50 dark:bg-gray-800/50 flex justify-between items-center">
<h2 class="text-lg font-semibold text-gray-900 dark:text-white flex items-center">
<span class="mr-2">🌉</span> {{ t('bridges') }}
</h2>
<button @click="addBridge"
class="inline-flex items-center px-3 py-1.5 border border-transparent text-xs font-medium rounded-md shadow-sm text-white bg-primary-600 hover:bg-primary-700 focus:outline-none transition-colors">
<svg class="-ml-0.5 mr-2 h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"
fill="currentColor">
<path fill-rule="evenodd"
d="M10 3a1 1 0 011 1v5h5a1 1 0 110 2h-5v5a1 1 0 11-2 0v-5H4a1 1 0 110-2h5V4a1 1 0 011-1z"
clip-rule="evenodd" />
</svg>
{{ t('addBridge') }}
</button>
</div>
<div v-if="config.bridges.length === 0"
class="p-8 text-center text-gray-500 dark:text-gray-400">
No Bridges added yet.
</div>
<div v-else class="divide-y divide-gray-100 dark:divide-gray-700">
<div v-for="(br, index) in config.bridges" :key="index"
class="p-6 hover:bg-gray-50 dark:hover:bg-gray-700/30 transition-colors relative group">
<button @click="removeBridge(index)"
class="absolute top-4 right-4 text-gray-400 hover:text-red-500 opacity-0 group-hover:opacity-100 transition-all p-1">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20"
fill="currentColor">
<path fill-rule="evenodd"
d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z"
clip-rule="evenodd" />
</svg>
</button>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
<div>
<label
class="block text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-1">{{
t('interfaceName') }}</label>
<input type="text" v-model="br.name" placeholder="br0"
class="block w-full rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm">
</div>
<div>
<label
class="block text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-1">{{
t('interfaces') }}</label>
<input type="text" v-model="br.interfacesInput" placeholder="eth0, eth1"
class="block w-full rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm">
</div>
<div class="flex items-center pt-6">
<input type="checkbox" v-model="br.dhcp4" :id="'br-dhcp4-'+index"
class="h-4 w-4 text-primary-600 focus:ring-primary-500 border-gray-300 rounded">
<label :for="'br-dhcp4-'+index"
class="ml-2 block text-sm text-gray-900 dark:text-gray-300">DHCPv4</label>
</div>
<div v-if="!br.dhcp4">
<label
class="block text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-1">{{
t('ipAddresses') }}</label>
<input type="text" v-model="br.addressesInput" placeholder="192.168.1.10/24"
class="block w-full rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm">
</div>
</div>
</div>
</div>
</div>
<!-- Bonds -->
<div
class="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-100 dark:border-gray-700 overflow-hidden">
<div
class="px-6 py-4 border-b border-gray-100 dark:border-gray-700 bg-gray-50 dark:bg-gray-800/50 flex justify-between items-center">
<h2 class="text-lg font-semibold text-gray-900 dark:text-white flex items-center">
<span class="mr-2">🔗</span> {{ t('bonds') }}
</h2>
<button @click="addBond"
class="inline-flex items-center px-3 py-1.5 border border-transparent text-xs font-medium rounded-md shadow-sm text-white bg-primary-600 hover:bg-primary-700 focus:outline-none transition-colors">
<svg class="-ml-0.5 mr-2 h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"
fill="currentColor">
<path fill-rule="evenodd"
d="M10 3a1 1 0 011 1v5h5a1 1 0 110 2h-5v5a1 1 0 11-2 0v-5H4a1 1 0 110-2h5V4a1 1 0 011-1z"
clip-rule="evenodd" />
</svg>
{{ t('addBond') }}
</button>
</div>
<div v-if="config.bonds.length === 0" class="p-8 text-center text-gray-500 dark:text-gray-400">
No Bonds added yet.
</div>
<div v-else class="divide-y divide-gray-100 dark:divide-gray-700">
<div v-for="(bond, index) in config.bonds" :key="index"
class="p-6 hover:bg-gray-50 dark:hover:bg-gray-700/30 transition-colors relative group">
<button @click="removeBond(index)"
class="absolute top-4 right-4 text-gray-400 hover:text-red-500 opacity-0 group-hover:opacity-100 transition-all p-1">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20"
fill="currentColor">
<path fill-rule="evenodd"
d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z"
clip-rule="evenodd" />
</svg>
</button>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
<div>
<label
class="block text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-1">{{
t('interfaceName') }}</label>
<input type="text" v-model="bond.name" placeholder="bond0"
class="block w-full rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm">
</div>
<div>
<label
class="block text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-1">{{
t('interfaces') }}</label>
<input type="text" v-model="bond.interfacesInput" placeholder="eth0, eth1"
class="block w-full rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm">
</div>
<div>
<label
class="block text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-1">{{
t('mode') }}</label>
<select v-model="bond.parameters.mode"
class="block w-full rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm">
<option value="balance-rr">balance-rr</option>
<option value="active-backup">active-backup</option>
<option value="balance-xor">balance-xor</option>
<option value="broadcast">broadcast</option>
<option value="802.3ad">802.3ad</option>
<option value="balance-tlb">balance-tlb</option>
<option value="balance-alb">balance-alb</option>
</select>
</div>
<div class="flex items-center pt-6">
<input type="checkbox" v-model="bond.dhcp4" :id="'bond-dhcp4-'+index"
class="h-4 w-4 text-primary-600 focus:ring-primary-500 border-gray-300 rounded">
<label :for="'bond-dhcp4-'+index"
class="ml-2 block text-sm text-gray-900 dark:text-gray-300">DHCPv4</label>
</div>
<div v-if="!bond.dhcp4">
<label
class="block text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-1">{{
t('ipAddresses') }}</label>
<input type="text" v-model="bond.addressesInput" placeholder="192.168.1.10/24"
class="block w-full rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm">
</div>
</div>
</div>
</div>
</div>
<!-- VLANs -->
<div
class="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-100 dark:border-gray-700 overflow-hidden">
<div
class="px-6 py-4 border-b border-gray-100 dark:border-gray-700 bg-gray-50 dark:bg-gray-800/50 flex justify-between items-center">
<h2 class="text-lg font-semibold text-gray-900 dark:text-white flex items-center">
<span class="mr-2">🏷️</span> {{ t('vlans') }}
</h2>
<button @click="addVlan"
class="inline-flex items-center px-3 py-1.5 border border-transparent text-xs font-medium rounded-md shadow-sm text-white bg-primary-600 hover:bg-primary-700 focus:outline-none transition-colors">
<svg class="-ml-0.5 mr-2 h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"
fill="currentColor">
<path fill-rule="evenodd"
d="M10 3a1 1 0 011 1v5h5a1 1 0 110 2h-5v5a1 1 0 11-2 0v-5H4a1 1 0 110-2h5V4a1 1 0 011-1z"
clip-rule="evenodd" />
</svg>
{{ t('addVlan') }}
</button>
</div>
<div v-if="config.vlans.length === 0" class="p-8 text-center text-gray-500 dark:text-gray-400">
No VLANs added yet.
</div>
<div v-else class="divide-y divide-gray-100 dark:divide-gray-700">
<div v-for="(vlan, index) in config.vlans" :key="index"
class="p-6 hover:bg-gray-50 dark:hover:bg-gray-700/30 transition-colors relative group">
<button @click="removeVlan(index)"
class="absolute top-4 right-4 text-gray-400 hover:text-red-500 opacity-0 group-hover:opacity-100 transition-all p-1">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20"
fill="currentColor">
<path fill-rule="evenodd"
d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z"
clip-rule="evenodd" />
</svg>
</button>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
<div>
<label
class="block text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-1">{{
t('interfaceName') }}</label>
<input type="text" v-model="vlan.name" placeholder="vlan10"
class="block w-full rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm">
</div>
<div>
<label
class="block text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-1">{{
t('id') }}</label>
<input type="number" v-model.number="vlan.id" placeholder="10"
class="block w-full rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm">
</div>
<div>
<label
class="block text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-1">{{
t('link') }}</label>
<input type="text" v-model="vlan.link" placeholder="eth0"
class="block w-full rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm">
</div>
<div class="flex items-center pt-6">
<input type="checkbox" v-model="vlan.dhcp4" :id="'vlan-dhcp4-'+index"
class="h-4 w-4 text-primary-600 focus:ring-primary-500 border-gray-300 rounded">
<label :for="'vlan-dhcp4-'+index"
class="ml-2 block text-sm text-gray-900 dark:text-gray-300">DHCPv4</label>
</div>
<div v-if="!vlan.dhcp4">
<label
class="block text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-1">{{
t('ipAddresses') }}</label>
<input type="text" v-model="vlan.addressesInput" placeholder="192.168.10.1/24"
class="block w-full rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm">
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Preview Sidebar -->
<div class="lg:col-span-5 space-y-6">
<div class="sticky top-24">
<div class="bg-gray-900 rounded-xl shadow-2xl overflow-hidden border border-gray-700">
<div
class="bg-gray-800 px-4 py-3 border-b border-gray-700 flex justify-between items-center">
<div class="flex space-x-2">
<div class="w-3 h-3 rounded-full bg-red-500"></div>
<div class="w-3 h-3 rounded-full bg-yellow-500"></div>
<div class="w-3 h-3 rounded-full bg-green-500"></div>
</div>
<span class="text-xs text-gray-400 font-mono">01-netcfg.yaml</span>
</div>
<div class="relative group">
<pre
class="p-6 text-sm font-mono leading-relaxed text-gray-300 overflow-x-auto min-h-[400px] max-h-[calc(100vh-300px)] scrollbar-thin scrollbar-thumb-gray-600 scrollbar-track-transparent"><code>{{ generatedYaml }}</code></pre>
<div
class="absolute top-4 right-4 opacity-0 group-hover:opacity-100 transition-opacity">
<button @click="copyToClipboard"
class="bg-gray-700 hover:bg-gray-600 text-white p-2 rounded-lg shadow-lg transition-colors"
title="Kopyala">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none"
viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" />
</svg>
</button>
</div>
</div>
</div>
<div class="mt-6 flex space-x-4">
<button @click="downloadYaml"
class="flex-1 bg-primary-600 hover:bg-primary-700 text-white font-medium py-3 px-4 rounded-xl shadow-lg shadow-primary-500/30 flex justify-center items-center transition-all transform hover:-translate-y-0.5">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" viewBox="0 0 20 20"
fill="currentColor">
<path fill-rule="evenodd"
d="M3 17a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zm3.293-7.707a1 1 0 011.414 0L9 10.586V3a1 1 0 112 0v7.586l1.293-1.293a1 1 0 111.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z"
clip-rule="evenodd" />
</svg>
{{ t('downloadYaml') }}
</button>
<button @click="copyToClipboard"
class="flex-1 bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-200 font-medium py-3 px-4 rounded-xl shadow-lg border border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700 flex justify-center items-center transition-all transform hover:-translate-y-0.5">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" fill="none"
viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3" />
</svg>
{{ t('copyClipboard') }}
</button>
</div>
</div>
<!-- Instructions -->
<div
class="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-100 dark:border-gray-700 overflow-hidden">
<div
class="px-6 py-4 border-b border-gray-100 dark:border-gray-700 bg-gray-50 dark:bg-gray-800/50">
<h3 class="font-semibold text-gray-900 dark:text-white flex items-center">
<span class="mr-2">📝</span> {{ t('instructions') }}
</h3>
</div>
<div class="p-6 text-sm text-gray-600 dark:text-gray-300 space-y-4">
<p>{{ t('step1') }}</p>
<div>
<p class="mb-2">{{ t('step2') }}</p>
<div
class="bg-gray-100 dark:bg-gray-900 p-2 rounded border border-gray-200 dark:border-gray-700 font-mono text-xs select-all break-all">
/etc/netplan/01-netcfg.yaml
</div>
</div>
<div>
<p class="mb-2">{{ t('step3') }}</p>
<div
class="bg-gray-100 dark:bg-gray-900 p-2 rounded border border-gray-200 dark:border-gray-700 font-mono text-xs select-all">
sudo chmod 600 /etc/netplan/01-netcfg.yaml
</div>
</div>
<div>
<p class="mb-2">{{ t('step4') }}</p>
<div
class="bg-gray-100 dark:bg-gray-900 p-2 rounded border border-gray-200 dark:border-gray-700 font-mono text-xs select-all">
sudo netplan try
</div>
</div>
<div>
<p class="mb-2">{{ t('step5') }}</p>
<div
class="bg-gray-100 dark:bg-gray-900 p-2 rounded border border-gray-200 dark:border-gray-700 font-mono text-xs select-all">
sudo netplan apply
</div>
</div>
<div class="bg-yellow-50 dark:bg-yellow-900/30 border-l-4 border-yellow-400 p-3">
<p class="text-xs text-yellow-700 dark:text-yellow-200">
{{ t('note') }}
</p>
</div>
</div>
</div>
</div>
</div>
</main>
<footer class="mt-auto py-8 text-center text-sm text-gray-500 dark:text-gray-400">
<p>Netplan Config Generator &copy; 2024</p>
</footer>
</div>
<script src="app.js"></script>
</body>
</html>