c9fd4aa84c
- Añade enricher.go + directorio enrichers/ para enriquecer entidades con fuentes externas. - Nuevos componentes frontend: IngestPanel (panel de ingesta de datos) y NodeContextMenu (menu contextual sobre nodos del grafo). - Retira SearchBar y lib/utils.ts; la busqueda se integra dentro de los paneles existentes. - Ajusta tipos (types.go, types.ts, wailsjs/go) y theming (postcss + app.css + Mantine). - Actualiza app.go y wails.json para exponer las nuevas capacidades. - Añade directorio projects/ con estado inicial. - Rebuild del frontend (dist actualizado).
109 lines
3.3 KiB
TypeScript
109 lines
3.3 KiB
TypeScript
import { useState } from 'react'
|
|
import { Stack, Group, TextInput, Textarea, Text, NumberInput } from '@mantine/core'
|
|
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, SimpleSelect, Button } from '@fn_library'
|
|
import type { Entity, RelationInputDTO } from '../types'
|
|
|
|
interface Props {
|
|
entities: Entity[]
|
|
relationPresets: string[]
|
|
onSubmit: (input: RelationInputDTO) => void
|
|
onClose: () => void
|
|
}
|
|
|
|
export function RelationDialog({ entities, relationPresets, onSubmit, onClose }: Props) {
|
|
const [name, setName] = useState(relationPresets[0] ?? '')
|
|
const [fromEntity, setFromEntity] = useState(entities[0]?.id ?? '')
|
|
const [toEntity, setToEntity] = useState(entities[1]?.id ?? entities[0]?.id ?? '')
|
|
const [description, setDescription] = useState('')
|
|
const [weight, setWeight] = useState<number | string>(1.0)
|
|
const [notes, setNotes] = useState('')
|
|
|
|
const handleSubmit = () => {
|
|
const w = typeof weight === 'number' ? weight : parseFloat(String(weight))
|
|
onSubmit({
|
|
name,
|
|
from_entity: fromEntity,
|
|
to_entity: toEntity,
|
|
description,
|
|
weight: isNaN(w) ? null : w,
|
|
tags: [],
|
|
notes,
|
|
})
|
|
}
|
|
|
|
return (
|
|
<Dialog open onOpenChange={(open: boolean) => { if (!open) onClose() }}>
|
|
<DialogContent>
|
|
<DialogHeader>
|
|
<DialogTitle>New Relation</DialogTitle>
|
|
</DialogHeader>
|
|
|
|
<Stack gap="sm">
|
|
<div>
|
|
<Text size="sm" fw={500} mb={4}>Relation Type</Text>
|
|
<SimpleSelect
|
|
value={name}
|
|
onValueChange={setName}
|
|
options={relationPresets.map(p => ({ value: p, label: p }))}
|
|
/>
|
|
</div>
|
|
|
|
<Group gap="sm" grow>
|
|
<div>
|
|
<Text size="sm" fw={500} mb={4}>From</Text>
|
|
<SimpleSelect
|
|
value={fromEntity}
|
|
onValueChange={setFromEntity}
|
|
options={entities.map(e => ({ value: e.id, label: e.name }))}
|
|
/>
|
|
</div>
|
|
<div>
|
|
<Text size="sm" fw={500} mb={4}>To</Text>
|
|
<SimpleSelect
|
|
value={toEntity}
|
|
onValueChange={setToEntity}
|
|
options={entities.map(e => ({ value: e.id, label: e.name }))}
|
|
/>
|
|
</div>
|
|
</Group>
|
|
|
|
<TextInput
|
|
label="Description"
|
|
value={description}
|
|
onChange={e => setDescription(e.currentTarget.value)}
|
|
size="sm"
|
|
/>
|
|
|
|
<NumberInput
|
|
label="Weight (0.0 - 1.0)"
|
|
value={weight}
|
|
onChange={setWeight}
|
|
step={0.1}
|
|
min={0}
|
|
max={1}
|
|
decimalScale={2}
|
|
size="sm"
|
|
/>
|
|
|
|
<Textarea
|
|
label="Notes"
|
|
value={notes}
|
|
onChange={e => setNotes(e.currentTarget.value)}
|
|
rows={2}
|
|
size="sm"
|
|
/>
|
|
</Stack>
|
|
|
|
<DialogFooter>
|
|
<Group justify="flex-end" gap="sm" mt="md">
|
|
<Button variant="secondary" onClick={onClose}>Cancel</Button>
|
|
<Button onClick={handleSubmit} disabled={!name || fromEntity === toEntity}>
|
|
Create
|
|
</Button>
|
|
</Group>
|
|
</DialogFooter>
|
|
</DialogContent>
|
|
</Dialog>
|
|
)
|
|
}
|