feat(ui): add relationship management features in schema editor
- Implement functionality to create, update, delete, and view relationships between tables. - Introduce new UI screens for managing relationships, including forms for adding and editing relationships. - Enhance table editor with navigation to relationship management. - Ensure relationships are displayed in a structured table format for better usability.
This commit is contained in:
57
pkg/ui/doc.go
Normal file
57
pkg/ui/doc.go
Normal file
@@ -0,0 +1,57 @@
|
||||
// Package ui provides an interactive terminal user interface (TUI) for editing database schemas.
|
||||
//
|
||||
// # Overview
|
||||
//
|
||||
// The ui package implements a full-featured terminal-based schema editor using tview,
|
||||
// allowing users to visually create, modify, and manage database schemas without writing
|
||||
// code or SQL.
|
||||
//
|
||||
// # Features
|
||||
//
|
||||
// The schema editor supports:
|
||||
// - Database management: Edit name, description, and properties
|
||||
// - Schema management: Create, edit, delete schemas
|
||||
// - Table management: Create, edit, delete tables
|
||||
// - Column management: Add, modify, delete columns with full property support
|
||||
// - Relationship management: Define and edit table relationships
|
||||
// - Domain management: Organize tables into logical domains
|
||||
// - Import & merge: Combine schemas from multiple sources
|
||||
// - Save: Export to any supported format
|
||||
//
|
||||
// # Architecture
|
||||
//
|
||||
// The package is organized into several components:
|
||||
// - editor.go: Main editor and application lifecycle
|
||||
// - *_screens.go: UI screens for each entity type
|
||||
// - *_dataops.go: Business logic and data operations
|
||||
// - dialogs.go: Reusable dialog components
|
||||
// - load_save_screens.go: File I/O and format selection
|
||||
// - main_menu.go: Primary navigation menu
|
||||
//
|
||||
// # Usage
|
||||
//
|
||||
// editor := ui.NewSchemaEditor(database)
|
||||
// if err := editor.Run(); err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// Or with pre-configured load/save options:
|
||||
//
|
||||
// editor := ui.NewSchemaEditorWithConfigs(database, loadConfig, saveConfig)
|
||||
// if err := editor.Run(); err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// # Navigation
|
||||
//
|
||||
// - Arrow keys: Navigate between items
|
||||
// - Enter: Select/edit item
|
||||
// - Tab/Shift+Tab: Navigate between buttons
|
||||
// - Escape: Go back/cancel
|
||||
// - Letter shortcuts: Quick actions (e.g., 'n' for new, 'e' for edit, 'd' for delete)
|
||||
//
|
||||
// # Integration
|
||||
//
|
||||
// The editor integrates with all readers and writers, supporting load/save operations
|
||||
// for any format supported by RelSpec (DBML, PostgreSQL, GORM, Prisma, etc.).
|
||||
package ui
|
||||
115
pkg/ui/relation_dataops.go
Normal file
115
pkg/ui/relation_dataops.go
Normal file
@@ -0,0 +1,115 @@
|
||||
package ui
|
||||
|
||||
import "git.warky.dev/wdevs/relspecgo/pkg/models"
|
||||
|
||||
// Relationship data operations - business logic for relationship management
|
||||
|
||||
// CreateRelationship creates a new relationship and adds it to a table
|
||||
func (se *SchemaEditor) CreateRelationship(schemaIndex, tableIndex int, rel *models.Relationship) *models.Relationship {
|
||||
if schemaIndex < 0 || schemaIndex >= len(se.db.Schemas) {
|
||||
return nil
|
||||
}
|
||||
|
||||
schema := se.db.Schemas[schemaIndex]
|
||||
if tableIndex < 0 || tableIndex >= len(schema.Tables) {
|
||||
return nil
|
||||
}
|
||||
|
||||
table := schema.Tables[tableIndex]
|
||||
if table.Relationships == nil {
|
||||
table.Relationships = make(map[string]*models.Relationship)
|
||||
}
|
||||
|
||||
table.Relationships[rel.Name] = rel
|
||||
table.UpdateDate()
|
||||
return rel
|
||||
}
|
||||
|
||||
// UpdateRelationship updates an existing relationship
|
||||
func (se *SchemaEditor) UpdateRelationship(schemaIndex, tableIndex int, oldName string, rel *models.Relationship) bool {
|
||||
if schemaIndex < 0 || schemaIndex >= len(se.db.Schemas) {
|
||||
return false
|
||||
}
|
||||
|
||||
schema := se.db.Schemas[schemaIndex]
|
||||
if tableIndex < 0 || tableIndex >= len(schema.Tables) {
|
||||
return false
|
||||
}
|
||||
|
||||
table := schema.Tables[tableIndex]
|
||||
if table.Relationships == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// Delete old entry if name changed
|
||||
if oldName != rel.Name {
|
||||
delete(table.Relationships, oldName)
|
||||
}
|
||||
|
||||
table.Relationships[rel.Name] = rel
|
||||
table.UpdateDate()
|
||||
return true
|
||||
}
|
||||
|
||||
// DeleteRelationship removes a relationship from a table
|
||||
func (se *SchemaEditor) DeleteRelationship(schemaIndex, tableIndex int, relName string) bool {
|
||||
if schemaIndex < 0 || schemaIndex >= len(se.db.Schemas) {
|
||||
return false
|
||||
}
|
||||
|
||||
schema := se.db.Schemas[schemaIndex]
|
||||
if tableIndex < 0 || tableIndex >= len(schema.Tables) {
|
||||
return false
|
||||
}
|
||||
|
||||
table := schema.Tables[tableIndex]
|
||||
if table.Relationships == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
delete(table.Relationships, relName)
|
||||
table.UpdateDate()
|
||||
return true
|
||||
}
|
||||
|
||||
// GetRelationship returns a relationship by name
|
||||
func (se *SchemaEditor) GetRelationship(schemaIndex, tableIndex int, relName string) *models.Relationship {
|
||||
if schemaIndex < 0 || schemaIndex >= len(se.db.Schemas) {
|
||||
return nil
|
||||
}
|
||||
|
||||
schema := se.db.Schemas[schemaIndex]
|
||||
if tableIndex < 0 || tableIndex >= len(schema.Tables) {
|
||||
return nil
|
||||
}
|
||||
|
||||
table := schema.Tables[tableIndex]
|
||||
if table.Relationships == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return table.Relationships[relName]
|
||||
}
|
||||
|
||||
// GetRelationshipNames returns all relationship names for a table
|
||||
func (se *SchemaEditor) GetRelationshipNames(schemaIndex, tableIndex int) []string {
|
||||
if schemaIndex < 0 || schemaIndex >= len(se.db.Schemas) {
|
||||
return nil
|
||||
}
|
||||
|
||||
schema := se.db.Schemas[schemaIndex]
|
||||
if tableIndex < 0 || tableIndex >= len(schema.Tables) {
|
||||
return nil
|
||||
}
|
||||
|
||||
table := schema.Tables[tableIndex]
|
||||
if table.Relationships == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
names := make([]string, 0, len(table.Relationships))
|
||||
for name := range table.Relationships {
|
||||
names = append(names, name)
|
||||
}
|
||||
return names
|
||||
}
|
||||
486
pkg/ui/relation_screens.go
Normal file
486
pkg/ui/relation_screens.go
Normal file
@@ -0,0 +1,486 @@
|
||||
package ui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/gdamore/tcell/v2"
|
||||
"github.com/rivo/tview"
|
||||
|
||||
"git.warky.dev/wdevs/relspecgo/pkg/models"
|
||||
)
|
||||
|
||||
// showRelationshipList displays all relationships for a table
|
||||
func (se *SchemaEditor) showRelationshipList(schemaIndex, tableIndex int) {
|
||||
table := se.GetTable(schemaIndex, tableIndex)
|
||||
if table == nil {
|
||||
return
|
||||
}
|
||||
|
||||
flex := tview.NewFlex().SetDirection(tview.FlexRow)
|
||||
|
||||
// Title
|
||||
title := tview.NewTextView().
|
||||
SetText(fmt.Sprintf("[::b]Relationships for Table: %s", table.Name)).
|
||||
SetDynamicColors(true).
|
||||
SetTextAlign(tview.AlignCenter)
|
||||
|
||||
// Create relationships table
|
||||
relTable := tview.NewTable().SetBorders(true).SetSelectable(true, false).SetFixed(1, 0)
|
||||
|
||||
// Add header row
|
||||
headers := []string{"Name", "Type", "From Columns", "To Table", "To Columns", "Description"}
|
||||
headerWidths := []int{20, 15, 20, 20, 20}
|
||||
for i, header := range headers {
|
||||
padding := ""
|
||||
if i < len(headerWidths) {
|
||||
padding = strings.Repeat(" ", headerWidths[i]-len(header))
|
||||
}
|
||||
cell := tview.NewTableCell(header + padding).
|
||||
SetTextColor(tcell.ColorYellow).
|
||||
SetSelectable(false).
|
||||
SetAlign(tview.AlignLeft)
|
||||
relTable.SetCell(0, i, cell)
|
||||
}
|
||||
|
||||
// Get relationship names
|
||||
relNames := se.GetRelationshipNames(schemaIndex, tableIndex)
|
||||
for row, relName := range relNames {
|
||||
rel := table.Relationships[relName]
|
||||
|
||||
// Name
|
||||
nameStr := fmt.Sprintf("%-20s", rel.Name)
|
||||
nameCell := tview.NewTableCell(nameStr).SetSelectable(true)
|
||||
relTable.SetCell(row+1, 0, nameCell)
|
||||
|
||||
// Type
|
||||
typeStr := fmt.Sprintf("%-15s", string(rel.Type))
|
||||
typeCell := tview.NewTableCell(typeStr).SetSelectable(true)
|
||||
relTable.SetCell(row+1, 1, typeCell)
|
||||
|
||||
// From Columns
|
||||
fromColsStr := strings.Join(rel.FromColumns, ", ")
|
||||
fromColsStr = fmt.Sprintf("%-20s", fromColsStr)
|
||||
fromColsCell := tview.NewTableCell(fromColsStr).SetSelectable(true)
|
||||
relTable.SetCell(row+1, 2, fromColsCell)
|
||||
|
||||
// To Table
|
||||
toTableStr := rel.ToTable
|
||||
if rel.ToSchema != "" && rel.ToSchema != table.Schema {
|
||||
toTableStr = rel.ToSchema + "." + rel.ToTable
|
||||
}
|
||||
toTableStr = fmt.Sprintf("%-20s", toTableStr)
|
||||
toTableCell := tview.NewTableCell(toTableStr).SetSelectable(true)
|
||||
relTable.SetCell(row+1, 3, toTableCell)
|
||||
|
||||
// To Columns
|
||||
toColsStr := strings.Join(rel.ToColumns, ", ")
|
||||
toColsStr = fmt.Sprintf("%-20s", toColsStr)
|
||||
toColsCell := tview.NewTableCell(toColsStr).SetSelectable(true)
|
||||
relTable.SetCell(row+1, 4, toColsCell)
|
||||
|
||||
// Description
|
||||
descCell := tview.NewTableCell(rel.Description).SetSelectable(true)
|
||||
relTable.SetCell(row+1, 5, descCell)
|
||||
}
|
||||
|
||||
relTable.SetTitle(" Relationships ").SetBorder(true).SetTitleAlign(tview.AlignLeft)
|
||||
|
||||
// Action buttons
|
||||
btnFlex := tview.NewFlex()
|
||||
btnNew := tview.NewButton("New Relationship [n]").SetSelectedFunc(func() {
|
||||
se.showNewRelationshipDialog(schemaIndex, tableIndex)
|
||||
})
|
||||
btnEdit := tview.NewButton("Edit [e]").SetSelectedFunc(func() {
|
||||
row, _ := relTable.GetSelection()
|
||||
if row > 0 && row <= len(relNames) {
|
||||
relName := relNames[row-1]
|
||||
se.showEditRelationshipDialog(schemaIndex, tableIndex, relName)
|
||||
}
|
||||
})
|
||||
btnDelete := tview.NewButton("Delete [d]").SetSelectedFunc(func() {
|
||||
row, _ := relTable.GetSelection()
|
||||
if row > 0 && row <= len(relNames) {
|
||||
relName := relNames[row-1]
|
||||
se.showDeleteRelationshipConfirm(schemaIndex, tableIndex, relName)
|
||||
}
|
||||
})
|
||||
btnBack := tview.NewButton("Back [b]").SetSelectedFunc(func() {
|
||||
se.pages.RemovePage("relationships")
|
||||
se.pages.SwitchToPage("table-editor")
|
||||
})
|
||||
|
||||
// Set up button navigation
|
||||
btnNew.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
|
||||
if event.Key() == tcell.KeyBacktab {
|
||||
se.app.SetFocus(relTable)
|
||||
return nil
|
||||
}
|
||||
if event.Key() == tcell.KeyTab {
|
||||
se.app.SetFocus(btnEdit)
|
||||
return nil
|
||||
}
|
||||
return event
|
||||
})
|
||||
|
||||
btnEdit.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
|
||||
if event.Key() == tcell.KeyBacktab {
|
||||
se.app.SetFocus(btnNew)
|
||||
return nil
|
||||
}
|
||||
if event.Key() == tcell.KeyTab {
|
||||
se.app.SetFocus(btnDelete)
|
||||
return nil
|
||||
}
|
||||
return event
|
||||
})
|
||||
|
||||
btnDelete.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
|
||||
if event.Key() == tcell.KeyBacktab {
|
||||
se.app.SetFocus(btnEdit)
|
||||
return nil
|
||||
}
|
||||
if event.Key() == tcell.KeyTab {
|
||||
se.app.SetFocus(btnBack)
|
||||
return nil
|
||||
}
|
||||
return event
|
||||
})
|
||||
|
||||
btnBack.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
|
||||
if event.Key() == tcell.KeyBacktab {
|
||||
se.app.SetFocus(btnDelete)
|
||||
return nil
|
||||
}
|
||||
if event.Key() == tcell.KeyTab {
|
||||
se.app.SetFocus(relTable)
|
||||
return nil
|
||||
}
|
||||
return event
|
||||
})
|
||||
|
||||
btnFlex.AddItem(btnNew, 0, 1, true).
|
||||
AddItem(btnEdit, 0, 1, false).
|
||||
AddItem(btnDelete, 0, 1, false).
|
||||
AddItem(btnBack, 0, 1, false)
|
||||
|
||||
relTable.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
|
||||
if event.Key() == tcell.KeyEscape {
|
||||
se.pages.RemovePage("relationships")
|
||||
se.pages.SwitchToPage("table-editor")
|
||||
return nil
|
||||
}
|
||||
if event.Key() == tcell.KeyTab {
|
||||
se.app.SetFocus(btnNew)
|
||||
return nil
|
||||
}
|
||||
if event.Key() == tcell.KeyEnter {
|
||||
row, _ := relTable.GetSelection()
|
||||
if row > 0 && row <= len(relNames) {
|
||||
relName := relNames[row-1]
|
||||
se.showEditRelationshipDialog(schemaIndex, tableIndex, relName)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if event.Rune() == 'n' {
|
||||
se.showNewRelationshipDialog(schemaIndex, tableIndex)
|
||||
return nil
|
||||
}
|
||||
if event.Rune() == 'e' {
|
||||
row, _ := relTable.GetSelection()
|
||||
if row > 0 && row <= len(relNames) {
|
||||
relName := relNames[row-1]
|
||||
se.showEditRelationshipDialog(schemaIndex, tableIndex, relName)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if event.Rune() == 'd' {
|
||||
row, _ := relTable.GetSelection()
|
||||
if row > 0 && row <= len(relNames) {
|
||||
relName := relNames[row-1]
|
||||
se.showDeleteRelationshipConfirm(schemaIndex, tableIndex, relName)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if event.Rune() == 'b' {
|
||||
se.pages.RemovePage("relationships")
|
||||
se.pages.SwitchToPage("table-editor")
|
||||
return nil
|
||||
}
|
||||
return event
|
||||
})
|
||||
|
||||
flex.AddItem(title, 1, 0, false).
|
||||
AddItem(relTable, 0, 1, true).
|
||||
AddItem(btnFlex, 1, 0, false)
|
||||
|
||||
se.pages.AddPage("relationships", flex, true, true)
|
||||
}
|
||||
|
||||
// showNewRelationshipDialog shows dialog to create a new relationship
|
||||
func (se *SchemaEditor) showNewRelationshipDialog(schemaIndex, tableIndex int) {
|
||||
table := se.GetTable(schemaIndex, tableIndex)
|
||||
if table == nil {
|
||||
return
|
||||
}
|
||||
|
||||
form := tview.NewForm()
|
||||
|
||||
// Collect all tables for dropdown
|
||||
var allTables []string
|
||||
var tableMap []struct{ schemaIdx, tableIdx int }
|
||||
for si, schema := range se.db.Schemas {
|
||||
for ti, t := range schema.Tables {
|
||||
tableName := t.Name
|
||||
if schema.Name != table.Schema {
|
||||
tableName = schema.Name + "." + t.Name
|
||||
}
|
||||
allTables = append(allTables, tableName)
|
||||
tableMap = append(tableMap, struct{ schemaIdx, tableIdx int }{si, ti})
|
||||
}
|
||||
}
|
||||
|
||||
relName := ""
|
||||
relType := models.OneToMany
|
||||
fromColumns := ""
|
||||
toColumns := ""
|
||||
description := ""
|
||||
selectedTableIdx := 0
|
||||
|
||||
form.AddInputField("Name", "", 40, nil, func(value string) {
|
||||
relName = value
|
||||
})
|
||||
|
||||
form.AddDropDown("Type", []string{
|
||||
string(models.OneToOne),
|
||||
string(models.OneToMany),
|
||||
string(models.ManyToMany),
|
||||
}, 1, func(option string, optionIndex int) {
|
||||
relType = models.RelationType(option)
|
||||
})
|
||||
|
||||
form.AddInputField("From Columns (comma-separated)", "", 40, nil, func(value string) {
|
||||
fromColumns = value
|
||||
})
|
||||
|
||||
form.AddDropDown("To Table", allTables, 0, func(option string, optionIndex int) {
|
||||
selectedTableIdx = optionIndex
|
||||
})
|
||||
|
||||
form.AddInputField("To Columns (comma-separated)", "", 40, nil, func(value string) {
|
||||
toColumns = value
|
||||
})
|
||||
|
||||
form.AddInputField("Description", "", 60, nil, func(value string) {
|
||||
description = value
|
||||
})
|
||||
|
||||
form.AddButton("Save", func() {
|
||||
if relName == "" {
|
||||
return
|
||||
}
|
||||
|
||||
// Parse columns
|
||||
fromCols := strings.Split(fromColumns, ",")
|
||||
for i := range fromCols {
|
||||
fromCols[i] = strings.TrimSpace(fromCols[i])
|
||||
}
|
||||
|
||||
toCols := strings.Split(toColumns, ",")
|
||||
for i := range toCols {
|
||||
toCols[i] = strings.TrimSpace(toCols[i])
|
||||
}
|
||||
|
||||
// Get target table
|
||||
targetSchema := se.db.Schemas[tableMap[selectedTableIdx].schemaIdx]
|
||||
targetTable := targetSchema.Tables[tableMap[selectedTableIdx].tableIdx]
|
||||
|
||||
rel := models.InitRelationship(relName, relType)
|
||||
rel.FromTable = table.Name
|
||||
rel.FromSchema = table.Schema
|
||||
rel.FromColumns = fromCols
|
||||
rel.ToTable = targetTable.Name
|
||||
rel.ToSchema = targetTable.Schema
|
||||
rel.ToColumns = toCols
|
||||
rel.Description = description
|
||||
|
||||
se.CreateRelationship(schemaIndex, tableIndex, rel)
|
||||
|
||||
se.pages.RemovePage("new-relationship")
|
||||
se.pages.RemovePage("relationships")
|
||||
se.showRelationshipList(schemaIndex, tableIndex)
|
||||
})
|
||||
|
||||
form.AddButton("Back", func() {
|
||||
se.pages.RemovePage("new-relationship")
|
||||
})
|
||||
|
||||
form.SetBorder(true).SetTitle(" New Relationship ").SetTitleAlign(tview.AlignLeft)
|
||||
form.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
|
||||
if event.Key() == tcell.KeyEscape {
|
||||
se.pages.RemovePage("new-relationship")
|
||||
return nil
|
||||
}
|
||||
return event
|
||||
})
|
||||
|
||||
se.pages.AddPage("new-relationship", form, true, true)
|
||||
}
|
||||
|
||||
// showEditRelationshipDialog shows dialog to edit a relationship
|
||||
func (se *SchemaEditor) showEditRelationshipDialog(schemaIndex, tableIndex int, relName string) {
|
||||
table := se.GetTable(schemaIndex, tableIndex)
|
||||
if table == nil {
|
||||
return
|
||||
}
|
||||
|
||||
rel := se.GetRelationship(schemaIndex, tableIndex, relName)
|
||||
if rel == nil {
|
||||
return
|
||||
}
|
||||
|
||||
form := tview.NewForm()
|
||||
|
||||
// Collect all tables for dropdown
|
||||
var allTables []string
|
||||
var tableMap []struct{ schemaIdx, tableIdx int }
|
||||
selectedTableIdx := 0
|
||||
for si, schema := range se.db.Schemas {
|
||||
for ti, t := range schema.Tables {
|
||||
tableName := t.Name
|
||||
if schema.Name != table.Schema {
|
||||
tableName = schema.Name + "." + t.Name
|
||||
}
|
||||
allTables = append(allTables, tableName)
|
||||
tableMap = append(tableMap, struct{ schemaIdx, tableIdx int }{si, ti})
|
||||
|
||||
// Check if this is the current target table
|
||||
if t.Name == rel.ToTable && schema.Name == rel.ToSchema {
|
||||
selectedTableIdx = len(allTables) - 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
newName := rel.Name
|
||||
relType := rel.Type
|
||||
fromColumns := strings.Join(rel.FromColumns, ", ")
|
||||
toColumns := strings.Join(rel.ToColumns, ", ")
|
||||
description := rel.Description
|
||||
|
||||
form.AddInputField("Name", rel.Name, 40, nil, func(value string) {
|
||||
newName = value
|
||||
})
|
||||
|
||||
// Find initial type index
|
||||
typeIdx := 1 // OneToMany default
|
||||
typeOptions := []string{
|
||||
string(models.OneToOne),
|
||||
string(models.OneToMany),
|
||||
string(models.ManyToMany),
|
||||
}
|
||||
for i, opt := range typeOptions {
|
||||
if opt == string(rel.Type) {
|
||||
typeIdx = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
form.AddDropDown("Type", typeOptions, typeIdx, func(option string, optionIndex int) {
|
||||
relType = models.RelationType(option)
|
||||
})
|
||||
|
||||
form.AddInputField("From Columns (comma-separated)", fromColumns, 40, nil, func(value string) {
|
||||
fromColumns = value
|
||||
})
|
||||
|
||||
form.AddDropDown("To Table", allTables, selectedTableIdx, func(option string, optionIndex int) {
|
||||
selectedTableIdx = optionIndex
|
||||
})
|
||||
|
||||
form.AddInputField("To Columns (comma-separated)", toColumns, 40, nil, func(value string) {
|
||||
toColumns = value
|
||||
})
|
||||
|
||||
form.AddInputField("Description", rel.Description, 60, nil, func(value string) {
|
||||
description = value
|
||||
})
|
||||
|
||||
form.AddButton("Save", func() {
|
||||
if newName == "" {
|
||||
return
|
||||
}
|
||||
|
||||
// Parse columns
|
||||
fromCols := strings.Split(fromColumns, ",")
|
||||
for i := range fromCols {
|
||||
fromCols[i] = strings.TrimSpace(fromCols[i])
|
||||
}
|
||||
|
||||
toCols := strings.Split(toColumns, ",")
|
||||
for i := range toCols {
|
||||
toCols[i] = strings.TrimSpace(toCols[i])
|
||||
}
|
||||
|
||||
// Get target table
|
||||
targetSchema := se.db.Schemas[tableMap[selectedTableIdx].schemaIdx]
|
||||
targetTable := targetSchema.Tables[tableMap[selectedTableIdx].tableIdx]
|
||||
|
||||
updatedRel := models.InitRelationship(newName, relType)
|
||||
updatedRel.FromTable = table.Name
|
||||
updatedRel.FromSchema = table.Schema
|
||||
updatedRel.FromColumns = fromCols
|
||||
updatedRel.ToTable = targetTable.Name
|
||||
updatedRel.ToSchema = targetTable.Schema
|
||||
updatedRel.ToColumns = toCols
|
||||
updatedRel.Description = description
|
||||
updatedRel.GUID = rel.GUID
|
||||
|
||||
se.UpdateRelationship(schemaIndex, tableIndex, relName, updatedRel)
|
||||
|
||||
se.pages.RemovePage("edit-relationship")
|
||||
se.pages.RemovePage("relationships")
|
||||
se.showRelationshipList(schemaIndex, tableIndex)
|
||||
})
|
||||
|
||||
form.AddButton("Back", func() {
|
||||
se.pages.RemovePage("edit-relationship")
|
||||
})
|
||||
|
||||
form.SetBorder(true).SetTitle(" Edit Relationship ").SetTitleAlign(tview.AlignLeft)
|
||||
form.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
|
||||
if event.Key() == tcell.KeyEscape {
|
||||
se.pages.RemovePage("edit-relationship")
|
||||
return nil
|
||||
}
|
||||
return event
|
||||
})
|
||||
|
||||
se.pages.AddPage("edit-relationship", form, true, true)
|
||||
}
|
||||
|
||||
// showDeleteRelationshipConfirm shows confirmation dialog for deleting a relationship
|
||||
func (se *SchemaEditor) showDeleteRelationshipConfirm(schemaIndex, tableIndex int, relName string) {
|
||||
modal := tview.NewModal().
|
||||
SetText(fmt.Sprintf("Delete relationship '%s'? This action cannot be undone.", relName)).
|
||||
AddButtons([]string{"Cancel", "Delete"}).
|
||||
SetDoneFunc(func(buttonIndex int, buttonLabel string) {
|
||||
if buttonLabel == "Delete" {
|
||||
se.DeleteRelationship(schemaIndex, tableIndex, relName)
|
||||
se.pages.RemovePage("delete-relationship-confirm")
|
||||
se.pages.RemovePage("relationships")
|
||||
se.showRelationshipList(schemaIndex, tableIndex)
|
||||
} else {
|
||||
se.pages.RemovePage("delete-relationship-confirm")
|
||||
}
|
||||
})
|
||||
|
||||
modal.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
|
||||
if event.Key() == tcell.KeyEscape {
|
||||
se.pages.RemovePage("delete-relationship-confirm")
|
||||
return nil
|
||||
}
|
||||
return event
|
||||
})
|
||||
|
||||
se.pages.AddAndSwitchToPage("delete-relationship-confirm", modal, true)
|
||||
}
|
||||
@@ -270,6 +270,9 @@ func (se *SchemaEditor) showTableEditor(schemaIndex, tableIndex int, table *mode
|
||||
se.showColumnEditor(schemaIndex, tableIndex, row-1, column)
|
||||
}
|
||||
})
|
||||
btnRelations := tview.NewButton("Relations [r]").SetSelectedFunc(func() {
|
||||
se.showRelationshipList(schemaIndex, tableIndex)
|
||||
})
|
||||
btnDelTable := tview.NewButton("Delete Table [d]").SetSelectedFunc(func() {
|
||||
se.showDeleteTableConfirm(schemaIndex, tableIndex)
|
||||
})
|
||||
@@ -308,6 +311,18 @@ func (se *SchemaEditor) showTableEditor(schemaIndex, tableIndex int, table *mode
|
||||
se.app.SetFocus(btnEditColumn)
|
||||
return nil
|
||||
}
|
||||
if event.Key() == tcell.KeyTab {
|
||||
se.app.SetFocus(btnRelations)
|
||||
return nil
|
||||
}
|
||||
return event
|
||||
})
|
||||
|
||||
btnRelations.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
|
||||
if event.Key() == tcell.KeyBacktab {
|
||||
se.app.SetFocus(btnEditTable)
|
||||
return nil
|
||||
}
|
||||
if event.Key() == tcell.KeyTab {
|
||||
se.app.SetFocus(btnDelTable)
|
||||
return nil
|
||||
@@ -317,7 +332,7 @@ func (se *SchemaEditor) showTableEditor(schemaIndex, tableIndex int, table *mode
|
||||
|
||||
btnDelTable.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
|
||||
if event.Key() == tcell.KeyBacktab {
|
||||
se.app.SetFocus(btnEditTable)
|
||||
se.app.SetFocus(btnRelations)
|
||||
return nil
|
||||
}
|
||||
if event.Key() == tcell.KeyTab {
|
||||
@@ -342,6 +357,7 @@ func (se *SchemaEditor) showTableEditor(schemaIndex, tableIndex int, table *mode
|
||||
btnFlex.AddItem(btnNewCol, 0, 1, true).
|
||||
AddItem(btnEditColumn, 0, 1, false).
|
||||
AddItem(btnEditTable, 0, 1, false).
|
||||
AddItem(btnRelations, 0, 1, false).
|
||||
AddItem(btnDelTable, 0, 1, false).
|
||||
AddItem(btnBack, 0, 1, false)
|
||||
|
||||
@@ -373,6 +389,10 @@ func (se *SchemaEditor) showTableEditor(schemaIndex, tableIndex int, table *mode
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if event.Rune() == 'r' {
|
||||
se.showRelationshipList(schemaIndex, tableIndex)
|
||||
return nil
|
||||
}
|
||||
if event.Rune() == 'b' {
|
||||
se.pages.RemovePage("table-editor")
|
||||
se.pages.SwitchToPage("schema-editor")
|
||||
|
||||
Reference in New Issue
Block a user