104 lines
2.7 KiB
Vue
104 lines
2.7 KiB
Vue
<script setup lang="ts">
|
|
import { Dialog, InputText, Select, Button } from 'primevue'
|
|
|
|
const props = defineProps<{
|
|
visible: boolean
|
|
group?: any | null
|
|
}>()
|
|
|
|
const emit = defineEmits<{
|
|
(e: 'update:visible', val: boolean): void
|
|
(e: 'saved'): void
|
|
}>()
|
|
|
|
const connections = useConnectionStore()
|
|
|
|
const form = ref({
|
|
name: '',
|
|
parentId: null as number | null,
|
|
})
|
|
|
|
const saving = ref(false)
|
|
const error = ref('')
|
|
|
|
const isEdit = computed(() => !!props.group?.id)
|
|
const title = computed(() => isEdit.value ? 'Edit Group' : 'New Group')
|
|
|
|
const parentOptions = computed(() => {
|
|
const options = [{ label: 'No Parent (top-level)', value: null }]
|
|
// Flatten groups for parent selection, excluding self if editing
|
|
const addGroups = (groups: any[], prefix = '') => {
|
|
for (const g of groups) {
|
|
if (isEdit.value && g.id === props.group?.id) continue
|
|
options.push({ label: prefix + g.name, value: g.id })
|
|
if (g.children?.length) addGroups(g.children, prefix + g.name + ' / ')
|
|
}
|
|
}
|
|
addGroups(connections.groups)
|
|
return options
|
|
})
|
|
|
|
watch(() => props.visible, (v) => {
|
|
if (v) {
|
|
form.value = {
|
|
name: props.group?.name || '',
|
|
parentId: props.group?.parentId ?? null,
|
|
}
|
|
error.value = ''
|
|
}
|
|
})
|
|
|
|
async function save() {
|
|
error.value = ''
|
|
saving.value = true
|
|
try {
|
|
if (isEdit.value) {
|
|
await connections.updateGroup(props.group.id, form.value)
|
|
} else {
|
|
await connections.createGroup(form.value)
|
|
}
|
|
emit('saved')
|
|
emit('update:visible', false)
|
|
} catch (e: any) {
|
|
error.value = e.data?.message || 'Failed to save group'
|
|
} finally {
|
|
saving.value = false
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<Dialog
|
|
:visible="visible"
|
|
@update:visible="emit('update:visible', $event)"
|
|
:header="title"
|
|
:modal="true"
|
|
:closable="true"
|
|
:style="{ width: '380px' }"
|
|
>
|
|
<div class="space-y-4 pt-2">
|
|
<!-- Name -->
|
|
<div>
|
|
<label class="block text-sm text-gray-400 mb-1">Group Name *</label>
|
|
<InputText v-model="form.name" placeholder="Production Servers" class="w-full" autofocus />
|
|
</div>
|
|
|
|
<!-- Parent -->
|
|
<div>
|
|
<label class="block text-sm text-gray-400 mb-1">Parent Group</label>
|
|
<Select v-model="form.parentId" :options="parentOptions" optionLabel="label" optionValue="value" class="w-full" />
|
|
</div>
|
|
|
|
<!-- Error -->
|
|
<p v-if="error" class="text-red-400 text-sm">{{ error }}</p>
|
|
</div>
|
|
|
|
<template #footer>
|
|
<div class="flex justify-end gap-2">
|
|
<Button label="Cancel" severity="secondary" @click="emit('update:visible', false)" />
|
|
<Button :label="saving ? 'Saving...' : 'Save'" :disabled="saving || !form.name" @click="save" />
|
|
</div>
|
|
</template>
|
|
</Dialog>
|
|
</template>
|