package ui import ( "fmt" "strings" "github.com/gdamore/tcell/v2" "github.com/rivo/tview" "git.warky.dev/wdevs/relspecgo/pkg/models" ) // showSchemaList displays the schema management screen func (se *SchemaEditor) showSchemaList() { flex := tview.NewFlex().SetDirection(tview.FlexRow) // Title title := tview.NewTextView(). SetText("[::b]Manage Schemas"). SetDynamicColors(true). SetTextAlign(tview.AlignCenter) // Create schemas table schemaTable := tview.NewTable().SetBorders(true).SetSelectable(true, false).SetFixed(1, 0) // Add header row with padding for full width headers := []string{"Name", "Sequence", "Total Tables", "Total Sequences", "Total Views", "Description"} headerWidths := []int{20, 15, 20, 20, 15} // Last column takes remaining space 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) schemaTable.SetCell(0, i, cell) } // Add existing schemas for row, schema := range se.db.Schemas { schema := schema // capture for closure // Name - pad to 20 chars nameStr := fmt.Sprintf("%-20s", schema.Name) nameCell := tview.NewTableCell(nameStr).SetSelectable(true) schemaTable.SetCell(row+1, 0, nameCell) // Sequence - pad to 15 chars seqStr := fmt.Sprintf("%-15s", fmt.Sprintf("%d", schema.Sequence)) seqCell := tview.NewTableCell(seqStr).SetSelectable(true) schemaTable.SetCell(row+1, 1, seqCell) // Total Tables - pad to 20 chars tablesStr := fmt.Sprintf("%-20s", fmt.Sprintf("%d", len(schema.Tables))) tablesCell := tview.NewTableCell(tablesStr).SetSelectable(true) schemaTable.SetCell(row+1, 2, tablesCell) // Total Sequences - pad to 20 chars sequencesStr := fmt.Sprintf("%-20s", fmt.Sprintf("%d", len(schema.Sequences))) sequencesCell := tview.NewTableCell(sequencesStr).SetSelectable(true) schemaTable.SetCell(row+1, 3, sequencesCell) // Total Views - pad to 15 chars viewsStr := fmt.Sprintf("%-15s", fmt.Sprintf("%d", len(schema.Views))) viewsCell := tview.NewTableCell(viewsStr).SetSelectable(true) schemaTable.SetCell(row+1, 4, viewsCell) // Description - no padding, takes remaining space descCell := tview.NewTableCell(schema.Description).SetSelectable(true) schemaTable.SetCell(row+1, 5, descCell) } schemaTable.SetTitle(" Schemas ").SetBorder(true).SetTitleAlign(tview.AlignLeft) // Action buttons flex (define before input capture) btnFlex := tview.NewFlex() btnNewSchema := tview.NewButton("New Schema [n]").SetSelectedFunc(func() { se.showNewSchemaDialog() }) btnBack := tview.NewButton("Back [b]").SetSelectedFunc(func() { se.pages.SwitchToPage("main") se.pages.RemovePage("schemas") }) // Set up button input captures for Tab/Shift+Tab navigation btnNewSchema.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { if event.Key() == tcell.KeyBacktab { se.app.SetFocus(schemaTable) 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(btnNewSchema) return nil } if event.Key() == tcell.KeyTab { se.app.SetFocus(schemaTable) return nil } return event }) btnFlex.AddItem(btnNewSchema, 0, 1, true). AddItem(btnBack, 0, 1, false) schemaTable.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { if event.Key() == tcell.KeyEscape { se.pages.SwitchToPage("main") se.pages.RemovePage("schemas") return nil } if event.Key() == tcell.KeyTab { se.app.SetFocus(btnNewSchema) return nil } if event.Key() == tcell.KeyEnter { row, _ := schemaTable.GetSelection() if row > 0 && row <= len(se.db.Schemas) { // Skip header row schemaIndex := row - 1 se.showSchemaEditor(schemaIndex, se.db.Schemas[schemaIndex]) return nil } } if event.Rune() == 'n' { se.showNewSchemaDialog() return nil } if event.Rune() == 'b' { se.pages.SwitchToPage("main") se.pages.RemovePage("schemas") return nil } return event }) flex.AddItem(title, 1, 0, false). AddItem(schemaTable, 0, 1, true). AddItem(btnFlex, 1, 0, false) se.pages.AddPage("schemas", flex, true, true) } // showSchemaEditor shows the editor for a specific schema func (se *SchemaEditor) showSchemaEditor(index int, schema *models.Schema) { flex := tview.NewFlex().SetDirection(tview.FlexRow) // Title title := tview.NewTextView(). SetText(fmt.Sprintf("[::b]Schema: %s", schema.Name)). SetDynamicColors(true). SetTextAlign(tview.AlignCenter) // Schema info display info := tview.NewTextView().SetDynamicColors(true) info.SetText(fmt.Sprintf("Tables: %d | Description: %s", len(schema.Tables), schema.Description)) // Table list tableList := tview.NewList().ShowSecondaryText(true) for i, table := range schema.Tables { tableIndex := i table := table colCount := len(table.Columns) tableList.AddItem(table.Name, fmt.Sprintf("%d columns", colCount), rune('0'+i), func() { se.showTableEditor(index, tableIndex, table) }) } tableList.AddItem("[New Table]", "Add a new table to this schema", 'n', func() { se.showNewTableDialog(index) }) tableList.AddItem("[Edit Schema Info]", "Edit schema properties", 'e', func() { se.showEditSchemaDialog(index) }) tableList.AddItem("[Delete Schema]", "Delete this schema", 'd', func() { se.showDeleteSchemaConfirm(index) }) tableList.SetBorder(true).SetTitle(" Tables ").SetTitleAlign(tview.AlignLeft) // Action buttons (define before input capture) btnFlex := tview.NewFlex() btnNewTable := tview.NewButton("New Table [n]").SetSelectedFunc(func() { se.showNewTableDialog(index) }) btnBack := tview.NewButton("Back to Schemas [b]").SetSelectedFunc(func() { se.pages.RemovePage("schema-editor") se.pages.SwitchToPage("schemas") }) // Set up button input captures for Tab/Shift+Tab navigation btnNewTable.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { if event.Key() == tcell.KeyBacktab { se.app.SetFocus(tableList) 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(btnNewTable) return nil } if event.Key() == tcell.KeyTab { se.app.SetFocus(tableList) return nil } return event }) tableList.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { if event.Key() == tcell.KeyEscape { se.pages.RemovePage("schema-editor") se.pages.SwitchToPage("schemas") return nil } if event.Key() == tcell.KeyTab { se.app.SetFocus(btnNewTable) return nil } if event.Rune() == 'b' { se.pages.RemovePage("schema-editor") se.pages.SwitchToPage("schemas") } return event }) btnFlex.AddItem(btnNewTable, 0, 1, true). AddItem(btnBack, 0, 1, false) flex.AddItem(title, 1, 0, false). AddItem(info, 2, 0, false). AddItem(tableList, 0, 1, true). AddItem(btnFlex, 1, 0, false) se.pages.AddPage("schema-editor", flex, true, true) } // showNewSchemaDialog shows dialog to create a new schema func (se *SchemaEditor) showNewSchemaDialog() { form := tview.NewForm() schemaName := "" description := "" form.AddInputField("Schema Name", "", 40, nil, func(value string) { schemaName = value }) form.AddInputField("Description", "", 40, nil, func(value string) { description = value }) form.AddButton("Save", func() { if schemaName == "" { return } se.CreateSchema(schemaName, description) se.pages.RemovePage("new-schema") se.pages.RemovePage("schemas") se.showSchemaList() }) form.AddButton("Back", func() { se.pages.RemovePage("new-schema") se.pages.RemovePage("schemas") se.showSchemaList() }) form.SetBorder(true).SetTitle(" New Schema ").SetTitleAlign(tview.AlignLeft) form.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { if event.Key() == tcell.KeyEscape { se.showExitConfirmation("new-schema", "schemas") return nil } return event }) se.pages.AddPage("new-schema", form, true, true) } // showEditSchemaDialog shows dialog to edit schema properties func (se *SchemaEditor) showEditSchemaDialog(schemaIndex int) { schema := se.db.Schemas[schemaIndex] form := tview.NewForm() // Local variables to collect changes newName := schema.Name newOwner := schema.Owner newDescription := schema.Description form.AddInputField("Schema Name", schema.Name, 40, nil, func(value string) { newName = value }) form.AddInputField("Owner", schema.Owner, 40, nil, func(value string) { newOwner = value }) form.AddTextArea("Description", schema.Description, 40, 5, 0, func(value string) { newDescription = value }) form.AddButton("Save", func() { // Apply changes using dataops se.UpdateSchema(schemaIndex, newName, newOwner, newDescription) schema := se.db.Schemas[schemaIndex] se.pages.RemovePage("edit-schema") se.pages.RemovePage("schema-editor") se.showSchemaEditor(schemaIndex, schema) }) form.AddButton("Back", func() { // Discard changes - don't apply them schema := se.db.Schemas[schemaIndex] se.pages.RemovePage("edit-schema") se.pages.RemovePage("schema-editor") se.showSchemaEditor(schemaIndex, schema) }) form.SetBorder(true).SetTitle(" Edit Schema ").SetTitleAlign(tview.AlignLeft) form.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { if event.Key() == tcell.KeyEscape { se.showExitConfirmation("edit-schema", "schema-editor") return nil } return event }) se.pages.AddPage("edit-schema", form, true, true) }