fix: change back navigation to 'b' key and improve deletion confirmation with name verification
- Changed 'q' key to 'b' for back navigation in client details - Added 'esc' key binding for back navigation - Updated help text to reflect new key bindings - Created new DeleteConfirmModal component with name verification - User must type exact client name to confirm deletion (safety feature) - Improved modal styling with visual feedback (red/green indicators) - Case-sensitive name matching to prevent accidental deletions Fixes: wg-admin-az7
This commit is contained in:
197
internal/tui/components/delete-confirm.go
Normal file
197
internal/tui/components/delete-confirm.go
Normal file
@@ -0,0 +1,197 @@
|
||||
package components
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/charmbracelet/bubbles/textinput"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
)
|
||||
|
||||
// DeleteConfirmModel represents a deletion confirmation modal with name verification
|
||||
type DeleteConfirmModel struct {
|
||||
clientName string
|
||||
input textinput.Model
|
||||
Visible bool
|
||||
Width int
|
||||
Height int
|
||||
showErrorMessage bool
|
||||
}
|
||||
|
||||
// Styles
|
||||
var (
|
||||
deleteModalStyle = lipgloss.NewStyle().
|
||||
Border(lipgloss.RoundedBorder()).
|
||||
BorderForeground(lipgloss.Color("196")).
|
||||
Padding(1, 3).
|
||||
Background(lipgloss.Color("235"))
|
||||
deleteTitleStyle = lipgloss.NewStyle().
|
||||
Foreground(lipgloss.Color("226")).
|
||||
Bold(true)
|
||||
deleteMessageStyle = lipgloss.NewStyle().
|
||||
Foreground(lipgloss.Color("255")).
|
||||
Width(60)
|
||||
deleteWarningStyle = lipgloss.NewStyle().
|
||||
Foreground(lipgloss.Color("196")).
|
||||
Bold(true).
|
||||
MarginTop(1)
|
||||
deleteInputStyle = lipgloss.NewStyle().
|
||||
Foreground(lipgloss.Color("255")).
|
||||
Width(60)
|
||||
deleteHelpStyle = lipgloss.NewStyle().
|
||||
Foreground(lipgloss.Color("241")).
|
||||
MarginTop(1)
|
||||
deleteErrorStyle = lipgloss.NewStyle().
|
||||
Foreground(lipgloss.Color("196")).
|
||||
Bold(true).
|
||||
MarginTop(1)
|
||||
deleteSuccessStyle = lipgloss.NewStyle().
|
||||
Foreground(lipgloss.Color("46")).
|
||||
Bold(true).
|
||||
MarginTop(1)
|
||||
)
|
||||
|
||||
// NewDeleteConfirm creates a new deletion confirmation modal
|
||||
func NewDeleteConfirm(clientName string, width, height int) *DeleteConfirmModel {
|
||||
ti := textinput.New()
|
||||
ti.Placeholder = "Type client name to confirm"
|
||||
ti.Focus()
|
||||
ti.CharLimit = 100
|
||||
ti.Width = 40
|
||||
|
||||
return &DeleteConfirmModel{
|
||||
clientName: clientName,
|
||||
input: ti,
|
||||
Visible: true,
|
||||
Width: width,
|
||||
Height: height,
|
||||
showErrorMessage: false,
|
||||
}
|
||||
}
|
||||
|
||||
// Init initializes the deletion confirmation modal
|
||||
func (m *DeleteConfirmModel) Init() tea.Cmd {
|
||||
return textinput.Blink
|
||||
}
|
||||
|
||||
// Update handles messages for the deletion confirmation modal
|
||||
func (m *DeleteConfirmModel) Update(msg tea.Msg) (*DeleteConfirmModel, tea.Cmd) {
|
||||
var cmd tea.Cmd
|
||||
|
||||
switch msg := msg.(type) {
|
||||
case tea.KeyMsg:
|
||||
switch msg.String() {
|
||||
case "esc":
|
||||
m.Visible = false
|
||||
return m, nil
|
||||
case "enter":
|
||||
if m.input.Value() == m.clientName {
|
||||
m.Visible = false
|
||||
return m, nil
|
||||
}
|
||||
m.showErrorMessage = true
|
||||
return m, nil
|
||||
}
|
||||
}
|
||||
|
||||
m.input, cmd = m.input.Update(msg)
|
||||
return m, cmd
|
||||
}
|
||||
|
||||
// View renders the deletion confirmation modal
|
||||
func (m *DeleteConfirmModel) View() string {
|
||||
if !m.Visible {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Check if input matches client name
|
||||
matches := m.input.Value() == m.clientName
|
||||
|
||||
// Build warning section
|
||||
warningText := deleteWarningStyle.Render(
|
||||
fmt.Sprintf("⚠️ This will permanently delete client '%s'", m.clientName),
|
||||
)
|
||||
|
||||
// Build message section
|
||||
messageText := deleteMessageStyle.Render(
|
||||
fmt.Sprintf("This action cannot be undone. Please type the client name to confirm deletion."),
|
||||
)
|
||||
|
||||
// Build input section
|
||||
inputSection := lipgloss.JoinVertical(
|
||||
lipgloss.Left,
|
||||
"",
|
||||
deleteInputStyle.Render("Client name:"),
|
||||
m.input.View(),
|
||||
)
|
||||
|
||||
// Build status section
|
||||
var statusText string
|
||||
if matches {
|
||||
statusText = deleteSuccessStyle.Render("✓ Client name matches. Press Enter to confirm deletion.")
|
||||
} else if m.showErrorMessage {
|
||||
statusText = deleteErrorStyle.Render("✗ Client name does not match. Please try again.")
|
||||
} else if m.input.Value() != "" {
|
||||
statusText = deleteHelpStyle.Render("Client name does not match yet...")
|
||||
} else {
|
||||
statusText = deleteHelpStyle.Render("Type the client name to enable confirmation.")
|
||||
}
|
||||
|
||||
// Build help section
|
||||
helpText := deleteHelpStyle.Render("Esc to cancel • Enter to confirm (when name matches)")
|
||||
|
||||
// Build modal content
|
||||
content := lipgloss.JoinVertical(
|
||||
lipgloss.Left,
|
||||
deleteTitleStyle.Render("🗑️ Delete Client"),
|
||||
"",
|
||||
warningText,
|
||||
"",
|
||||
messageText,
|
||||
inputSection,
|
||||
statusText,
|
||||
"",
|
||||
helpText,
|
||||
)
|
||||
|
||||
// Apply modal style
|
||||
modal := deleteModalStyle.Render(content)
|
||||
|
||||
// Center modal on screen
|
||||
modalWidth := lipgloss.Width(modal)
|
||||
modalHeight := lipgloss.Height(modal)
|
||||
|
||||
x := (m.Width - modalWidth) / 2
|
||||
if x < 0 {
|
||||
x = 0
|
||||
}
|
||||
y := (m.Height - modalHeight) / 2
|
||||
if y < 0 {
|
||||
y = 0
|
||||
}
|
||||
|
||||
return lipgloss.Place(m.Width, m.Height,
|
||||
lipgloss.Left, lipgloss.Top,
|
||||
modal,
|
||||
lipgloss.WithWhitespaceChars(" "),
|
||||
lipgloss.WithWhitespaceForeground(lipgloss.Color("235")),
|
||||
)
|
||||
}
|
||||
|
||||
// IsConfirmed returns true if user confirmed the deletion
|
||||
func (m *DeleteConfirmModel) IsConfirmed() bool {
|
||||
return !m.Visible && m.input.Value() == m.clientName
|
||||
}
|
||||
|
||||
// IsCancelled returns true if user cancelled
|
||||
func (m *DeleteConfirmModel) IsCancelled() bool {
|
||||
return !m.Visible && m.input.Value() != m.clientName
|
||||
}
|
||||
|
||||
// Reset resets the modal for reuse
|
||||
func (m *DeleteConfirmModel) Reset() {
|
||||
m.input.SetValue("")
|
||||
m.input.Reset()
|
||||
m.Visible = true
|
||||
m.showErrorMessage = false
|
||||
}
|
||||
Reference in New Issue
Block a user