package models import ( "database/sql" "fmt" "simple_portal/database" ) // Card represents a navigation card on the portal home page. type Card struct { ID int `json:"id"` Icon string `json:"icon"` Title string `json:"title"` Subtitle string `json:"subtitle"` URL string `json:"url"` Sort int `json:"sort"` Enabled bool `json:"enabled"` CreatedAt string `json:"created_at"` } // GetAllCards returns all cards ordered by sort ascending. func GetAllCards() ([]Card, error) { rows, err := database.DB.Query("SELECT id, icon, title, subtitle, url, sort, enabled, created_at FROM cards ORDER BY sort ASC") if err != nil { return nil, fmt.Errorf("failed to query cards: %w", err) } defer rows.Close() var cards []Card for rows.Next() { var c Card var enabled int if err := rows.Scan(&c.ID, &c.Icon, &c.Title, &c.Subtitle, &c.URL, &c.Sort, &enabled, &c.CreatedAt); err != nil { return nil, fmt.Errorf("failed to scan card: %w", err) } c.Enabled = enabled == 1 cards = append(cards, c) } return cards, rows.Err() } // GetEnabledCards returns only enabled cards ordered by sort ascending. func GetEnabledCards() ([]Card, error) { rows, err := database.DB.Query("SELECT id, icon, title, subtitle, url, sort, enabled, created_at FROM cards WHERE enabled = 1 ORDER BY sort ASC") if err != nil { return nil, fmt.Errorf("failed to query enabled cards: %w", err) } defer rows.Close() var cards []Card for rows.Next() { var c Card var enabled int if err := rows.Scan(&c.ID, &c.Icon, &c.Title, &c.Subtitle, &c.URL, &c.Sort, &enabled, &c.CreatedAt); err != nil { return nil, fmt.Errorf("failed to scan card: %w", err) } c.Enabled = enabled == 1 cards = append(cards, c) } return cards, rows.Err() } // GetCardByID returns a single card by its ID. func GetCardByID(id int) (*Card, error) { var c Card var enabled int err := database.DB.QueryRow( "SELECT id, icon, title, subtitle, url, sort, enabled, created_at FROM cards WHERE id = ?", id, ).Scan(&c.ID, &c.Icon, &c.Title, &c.Subtitle, &c.URL, &c.Sort, &enabled, &c.CreatedAt) if err == sql.ErrNoRows { return nil, nil } if err != nil { return nil, fmt.Errorf("failed to query card by id: %w", err) } c.Enabled = enabled == 1 return &c, nil } // CreateCard inserts a new card with sort = MAX(sort) + 1. func CreateCard(card *Card) error { var maxSort sql.NullInt64 err := database.DB.QueryRow("SELECT MAX(sort) FROM cards").Scan(&maxSort) if err != nil { return fmt.Errorf("failed to get max sort: %w", err) } newSort := 0 if maxSort.Valid { newSort = int(maxSort.Int64) + 1 } enabledInt := 0 if card.Enabled { enabledInt = 1 } result, err := database.DB.Exec( "INSERT INTO cards (icon, title, subtitle, url, sort, enabled) VALUES (?, ?, ?, ?, ?, ?)", card.Icon, card.Title, card.Subtitle, card.URL, newSort, enabledInt, ) if err != nil { return fmt.Errorf("failed to insert card: %w", err) } lastID, err := result.LastInsertId() if err != nil { return fmt.Errorf("failed to get last insert id: %w", err) } card.ID = int(lastID) card.Sort = newSort return nil } // UpdateCard updates an existing card by ID. func UpdateCard(card *Card) error { enabledInt := 0 if card.Enabled { enabledInt = 1 } _, err := database.DB.Exec( "UPDATE cards SET icon = ?, title = ?, subtitle = ?, url = ?, sort = ?, enabled = ? WHERE id = ?", card.Icon, card.Title, card.Subtitle, card.URL, card.Sort, enabledInt, card.ID, ) if err != nil { return fmt.Errorf("failed to update card: %w", err) } return nil } // DeleteCard deletes a card by ID. func DeleteCard(id int) error { _, err := database.DB.Exec("DELETE FROM cards WHERE id = ?", id) if err != nil { return fmt.Errorf("failed to delete card: %w", err) } return nil } // ToggleCard toggles the enabled status of a card. func ToggleCard(id int) error { _, err := database.DB.Exec("UPDATE cards SET enabled = CASE WHEN enabled = 1 THEN 0 ELSE 1 END WHERE id = ?", id) if err != nil { return fmt.Errorf("failed to toggle card: %w", err) } return nil } // MoveCardUp swaps the sort value of the card with the one above it. func MoveCardUp(id int) error { // Get the current card card, err := GetCardByID(id) if err != nil || card == nil { return fmt.Errorf("card not found: %d", id) } // Find the card with the next lower sort value (the one above) var aboveCard Card var aboveEnabled int err = database.DB.QueryRow( "SELECT id, icon, title, subtitle, url, sort, enabled, created_at FROM cards WHERE sort < ? ORDER BY sort DESC LIMIT 1", card.Sort, ).Scan(&aboveCard.ID, &aboveCard.Icon, &aboveCard.Title, &aboveCard.Subtitle, &aboveCard.URL, &aboveCard.Sort, &aboveEnabled, &aboveCard.CreatedAt) if err == sql.ErrNoRows { // Already at the top return nil } if err != nil { return fmt.Errorf("failed to find card above: %w", err) } aboveCard.Enabled = aboveEnabled == 1 // Swap sort values _, err = database.DB.Exec("UPDATE cards SET sort = ? WHERE id = ?", aboveCard.Sort, card.ID) if err != nil { return fmt.Errorf("failed to swap sort (current): %w", err) } _, err = database.DB.Exec("UPDATE cards SET sort = ? WHERE id = ?", card.Sort, aboveCard.ID) if err != nil { return fmt.Errorf("failed to swap sort (above): %w", err) } return nil } // MoveCardDown swaps the sort value of the card with the one below it. func MoveCardDown(id int) error { // Get the current card card, err := GetCardByID(id) if err != nil || card == nil { return fmt.Errorf("card not found: %d", id) } // Find the card with the next higher sort value (the one below) var belowCard Card var belowEnabled int err = database.DB.QueryRow( "SELECT id, icon, title, subtitle, url, sort, enabled, created_at FROM cards WHERE sort > ? ORDER BY sort ASC LIMIT 1", card.Sort, ).Scan(&belowCard.ID, &belowCard.Icon, &belowCard.Title, &belowCard.Subtitle, &belowCard.URL, &belowCard.Sort, &belowEnabled, &belowCard.CreatedAt) if err == sql.ErrNoRows { // Already at the bottom return nil } if err != nil { return fmt.Errorf("failed to find card below: %w", err) } belowCard.Enabled = belowEnabled == 1 // Swap sort values _, err = database.DB.Exec("UPDATE cards SET sort = ? WHERE id = ?", belowCard.Sort, card.ID) if err != nil { return fmt.Errorf("failed to swap sort (current): %w", err) } _, err = database.DB.Exec("UPDATE cards SET sort = ? WHERE id = ?", card.Sort, belowCard.ID) if err != nil { return fmt.Errorf("failed to swap sort (below): %w", err) } return nil }