Files
relspecgo/pkg/ui/ui_rules.md
Hein 1795eb64d1
Some checks failed
CI / Test (1.24) (push) Failing after 1m3s
CI / Lint (push) Failing after -27m11s
CI / Build (push) Successful in 40s
Integration Tests / Integration Tests (push) Failing after -28m11s
CI / Test (1.25) (push) Failing after -26m33s
feat(ui): 🎨 Implement schema and table management screens
* Add schema management screen with list and editor
* Implement table management screen with list and editor
* Create data operations for schema and table management
* Define UI rules and guidelines for consistency
* Ensure circular tab navigation and keyboard shortcuts
* Add forms for creating and editing schemas and tables
* Implement confirmation dialogs for destructive actions
2026-01-04 18:29:29 +02:00

10 KiB

UI Rules and Guidelines

Layout Requirements

All layouts / forms must be in seperate files regarding their domain or entity.

Screen Layout Structure

All screens must follow this consistent layout:

  1. Title at the Top (1 row, fixed height)

    • Centered bold text: [::b]Title Text
    • Use tview.NewTextView() with SetTextAlign(tview.AlignCenter)
    • Enable dynamic colors: SetDynamicColors(true)
  2. Content in the Middle (flexible height)

    • Tables, lists, forms, or info displays
    • Uses flex weight of 1 for dynamic sizing
  3. Action Buttons at the Bottom (1 row, fixed height)

    • Must be in a horizontal flex container
    • Action buttons before Back button
    • Back button is always last

Form Layout Structure

All forms must follow this button order:

  1. Save Button (always first)

    • Label: "Save"
    • Primary action that commits changes
  2. Delete Button (optional, only for edit forms)

    • Label: "Delete"
    • Only shown when editing existing items (not for new items)
    • Must show confirmation dialog before deletion
  3. Back Button (always last)

    • Label: "Back"
    • Returns to previous screen without saving

Button Order Examples:

  • New Item Forms: Save, Back
  • Edit Item Forms: Save, Delete, Back

Tab Navigation

All screens must implement circular tab navigation:

  1. Tab Key - Moves focus to the next focusable element
  2. Shift+Tab (BackTab) - Moves focus to the previous focusable element
  3. At the End - Tab cycles back to the first element
  4. At the Start - Shift+Tab cycles back to the last element

Navigation Flow Pattern:

  • Each widget must handle both Tab and BackTab
  • First widget: BackTab → Last widget, Tab → Second widget
  • Middle widgets: BackTab → Previous widget, Tab → Next widget
  • Last widget: BackTab → Previous widget, Tab → First widget

Keyboard Shortcuts

Standard Keys

  • ESC - Cancel current operation or go back to previous screen
  • Tab - Move focus forward (circular)
  • Shift+Tab - Move focus backward (circular)
  • Enter - Activate/select current item in tables and lists

Letter Key Shortcuts

  • 'n' - New (create new item)
  • 'b' - Back (return to previous screen)
  • 'e' - Edit (edit current item)
  • 'd' - Delete (delete current item)
  • 'c' - Edit Column (in table editor)
  • 's' - Manage Schemas (in main menu)
  • 't' - Manage Tables (in main menu)
  • 'q' - Quit/Exit (in main menu)

Consistency Requirements

  1. Layout Structure - All screens: Title (top) → Content (middle) → Buttons (bottom)
  2. Title Format - Bold ([::b]), centered, dynamic colors enabled
  3. Tables - Fixed headers (row 0), borders enabled, selectable rows
  4. Buttons - Include keyboard shortcuts in labels (e.g., "Back [b]")
  5. Forms - Button order: Save, Delete (if edit), Back
  6. Destructive Actions - Always show confirmation dialogs
  7. ESC Key - All screens support ESC to go back
  8. Action Buttons - Positioned before Back button, in logical order
  9. Data Refresh - Always refresh the previous screen when returning from a form or dialog

Widget Naming Conventions

  • Tables: schemaTable, tableTable, colTable
  • Buttons: Prefix with btn (e.g., btnBack, btnDelete, btnNewSchema)
  • Flex containers: btnFlex for button containers, flex for main layout
  • Forms: form
  • Lists: list, tableList
  • Text views: title, info
  • Use camelCase for all variable names

Page Naming Conventions

Use descriptive kebab-case names:

  • Main screens: main, schemas, tables, schema-editor, table-editor, column-editor
  • Load/Save screens: load-database, save-database
  • Creation dialogs: new-schema, new-table, new-column, new-table-from-list
  • Edit dialogs: edit-schema, edit-table
  • Confirmations: confirm-delete-schema, confirm-delete-table, confirm-delete-column
  • Exit confirmations: exit-confirm, exit-editor-confirm
  • Status dialogs: error-dialog, success-dialog

Dialog and Confirmation Rules

Confirmation Dialogs

  1. Delete Confirmations - Required for all destructive actions

    • Show item name in confirmation text
    • Buttons: "Cancel", "Delete"
    • ESC key dismisses dialog
  2. Exit Confirmations - Required when exiting forms with potential unsaved changes

    • Text: "Exit without saving changes?"
    • Buttons: "Cancel", "No, exit without saving"
    • ESC key confirms exit
  3. Save Confirmations - Optional, based on context

    • Use for critical data changes
    • Clear description of what will be saved

Dialog Behavior

  • All dialogs must capture ESC key for dismissal
  • Modal dialogs overlay current screen
  • Confirmation dialogs use tview.NewModal()
  • Remove dialog page after action completes

Data Refresh Rules

When returning from any form or dialog, the previous screen must be refreshed to show updated data. If Tables exists in the screen, their data must be updated:

  1. After Save - Remove and recreate the previous screen to display updated data
  2. After Delete - Remove and recreate the previous screen to display remaining data
  3. After Cancel/Back - Remove and recreate the previous screen (data may have changed)
  4. Implementation Pattern - Remove the current page, remove the previous page, then recreate the previous page with fresh data

Why This Matters:

  • Ensures users see their changes immediately
  • Prevents stale data from being displayed
  • Maintains data consistency across the UI
  • Avoids confusion from seeing outdated information

Example Flow:

User on Schema List → Opens Edit Schema Form → Saves Changes →
Returns to Schema List (refreshed with updated schema data)

Big Loading/Saving Operations

When loading big changes, files or data, always give a load completed or load error dialog. Do the same with saving. This informs the user what happens. When data is dirty, always ask the user to save when trying to exit.

Load/Save Screens

  • Load Screen (load-database) - Shown when no source is specified via command line

    • Format dropdown (dbml, dctx, drawdb, graphql, json, yaml, gorm, bun, drizzle, prisma, typeorm, pgsql)
    • File path input (for file-based formats)
    • Connection string input (for database formats like pgsql)
    • Load button [l] - Loads the database
    • Create New button [n] - Creates a new empty database
    • Exit button [q] - Exits the application
    • ESC key exits the application
  • Save Screen (save-database) - Accessible from main menu with 'w' key

    • Format dropdown (same as load screen)
    • File path input
    • Help text explaining format requirements
    • Save button [s] - Saves the database
    • Back button [b] - Returns to main menu
    • ESC key returns to main menu
    • Pre-populated with existing save configuration if available

Status Dialogs

  • Error Dialog (error-dialog) - Shows error messages with OK button and ESC to dismiss
  • Success Dialog (success-dialog) - Shows success messages with OK button, ESC to dismiss, and optional callback on close

Screen Organization

Organize UI code into these files:

UI Files (Screens and Dialogs)

  • editor.go - Core SchemaEditor struct, constructor, Run() method, helper functions
  • main_menu.go - Main menu screen
  • load_save_screens.go - Database load and save screens
  • database_screens.go - Database edit form
  • schema_screens.go - Schema list, schema editor, new/edit schema dialogs
  • table_screens.go - Tables list, table editor, new/edit table dialogs
  • column_screens.go - Column editor, new column dialog
  • domain_screens.go - Domain list, domain editor, new/edit domain dialogs
  • dialogs.go - Confirmation dialogs (exit, delete)

Data Operations Files (Business Logic)

  • schema_dataops.go - Schema CRUD operations (Create, Read, Update, Delete)
  • table_dataops.go - Table CRUD operations
  • column_dataops.go - Column CRUD operations

Code Separation Rules

UI vs Business Logic

  1. UI Files - Handle only presentation and user interaction

    • Display data in tables, lists, and forms
    • Capture user input
    • Navigate between screens
    • Show/hide dialogs
    • Call dataops methods for actual data changes
  2. Dataops Files - Handle only business logic and data manipulation

    • Create, read, update, delete operations
    • Data validation
    • Data structure manipulation
    • Return created/updated objects or success/failure status
    • No UI code or tview references

Implementation Pattern

Creating New Items

Bad (Direct Data Manipulation in UI):

form.AddButton("Save", func() {
    schema := &models.Schema{Name: name, Description: desc, ...}
    se.db.Schemas = append(se.db.Schemas, schema)
})

Good (Using Dataops Methods):

form.AddButton("Save", func() {
    se.CreateSchema(name, description)
})

Editing Existing Items

Bad (Modifying Data in onChange Callbacks):

form.AddInputField("Name", column.Name, 40, nil, func(value string) {
    column.Name = value  // Changes immediately as user types!
})
form.AddButton("Save", func() {
    // Data already changed, just refresh screen
})

Good (Local Variables + Dataops on Save):

// Store original values
originalName := column.Name
newName := column.Name

form.AddInputField("Name", column.Name, 40, nil, func(value string) {
    newName = value  // Store in local variable
})

form.AddButton("Save", func() {
    // Apply changes only when Save is clicked
    se.UpdateColumn(schemaIndex, tableIndex, originalName, newName, ...)
    // Then refresh screen
})

form.AddButton("Back", func() {
    // Discard changes - don't apply local variables
    // Just refresh screen
})

Why This Matters

Edit Forms Must Use Local Variables:

  1. Deferred Changes - Changes only apply when Save is clicked
  2. Cancellable - Back button discards changes without saving
  3. Handles Renames - Original name preserved to update map keys correctly
  4. User Expectations - Save means "commit changes", Back means "cancel"

This separation ensures:

  • Cleaner, more maintainable code
  • Reusable business logic
  • Easier testing
  • Clear separation of concerns
  • Proper change management (save vs cancel)