150 lines
4.5 KiB
Vue
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>
|
|
|