browser-vault-gui/src/components/ServerSelector.vue
2025-10-20 19:34:11 +02:00

150 lines
4.5 KiB
Vue

<script setup lang="ts">
import { ref } from 'vue'
import type { VaultServer } from '../types'
interface Props {
servers: VaultServer[]
selectedServer: VaultServer | null
}
const props = defineProps<Props>()
const emit = defineEmits<{
addServer: [server: VaultServer]
removeServer: [serverId: string]
selectServer: [server: VaultServer]
}>()
const showAddForm = ref(false)
const newServer = ref({
name: '',
url: '',
description: '',
})
const handleSubmit = () => {
if (!newServer.value.name || !newServer.value.url) return
const server: VaultServer = {
id: newServer.value.name,
name: newServer.value.name,
url: newServer.value.url,
description: newServer.value.description || undefined,
}
emit('addServer', server)
newServer.value = { name: '', url: '', description: '' }
showAddForm.value = false
}
const handleRemove = (serverId: string, serverName: string) => {
if (confirm(`Remove server "${serverName}"?`)) {
emit('removeServer', serverId)
}
}
</script>
<template>
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<!-- Header -->
<div class="flex justify-between items-center mb-4">
<h2 class="card-title text-2xl">Vault Servers</h2>
<button
class="btn btn-primary btn-sm"
@click="showAddForm = !showAddForm"
>
{{ showAddForm ? 'Cancel' : '+ Add Server' }}
</button>
</div>
<!-- Add Server Form -->
<div v-if="showAddForm" class="bg-base-200 p-4 rounded-lg mb-4">
<form @submit.prevent="handleSubmit" class="space-y-4">
<div class="form-control">
<label class="label">
<span class="label-text">Server Name *</span>
</label>
<input
v-model="newServer.name"
type="text"
placeholder="Production Vault"
class="input input-bordered w-full"
required
/>
</div>
<div class="form-control">
<label class="label">
<span class="label-text">Server URL *</span>
</label>
<input
v-model="newServer.url"
type="url"
placeholder="https://vault.example.com"
class="input input-bordered w-full"
required
/>
</div>
<div class="form-control">
<label class="label">
<span class="label-text">Description</span>
</label>
<input
v-model="newServer.description"
type="text"
placeholder="Optional description"
class="input input-bordered w-full"
/>
</div>
<!-- KV v2 is enforced - no version selection needed -->
<button type="submit" class="btn btn-success w-full">
Add Server
</button>
</form>
</div>
<!-- Server List -->
<div v-if="servers.length === 0" class="text-center py-12 text-base-content/60">
<p class="text-lg mb-2">No vault servers configured yet.</p>
<p class="text-sm italic">Click "Add Server" to get started.</p>
</div>
<div v-else class="space-y-3">
<div
v-for="server in servers"
:key="server.id"
class="card bg-base-200 cursor-pointer hover:bg-base-300 transition-all duration-200"
:class="{ 'ring-2 ring-primary shadow-lg': selectedServer?.id === server.id }"
@click="emit('selectServer', server)"
>
<div class="card-body p-4 flex flex-row justify-between items-center">
<div class="flex-1">
<h3 class="font-bold text-lg">{{ server.name }}</h3>
<p class="text-sm font-mono opacity-70 mt-1">{{ server.url }}</p>
<p v-if="server.description" class="text-sm italic opacity-60 mt-1">
{{ server.description }}
</p>
<div class="mt-2 flex gap-2 flex-wrap">
<span class="badge badge-sm badge-outline">KV v2</span>
<span v-if="server.savedCredentials" class="badge badge-sm badge-warning">
🔓 Saved Credentials
</span>
</div>
</div>
<button
class="btn btn-error btn-sm"
@click.stop="handleRemove(server.id, server.name)"
>
Remove
</button>
</div>
</div>
</div>
</div>
</div>
</template>