package repository import ( "context" "encoding/json" "time" "github.com/google/uuid" "github.com/jackc/pgx/v5/pgxpool" ) // HomeTool is one homepage grid entry. type HomeTool struct { ID uuid.UUID `json:"id"` RowIndex int `json:"row_index"` SortOrder int `json:"sort_order"` Path string `json:"path"` Icon string `json:"icon"` Label string `json:"label"` Badge *string `json:"badge,omitempty"` BadgeTone *string `json:"badge_tone,omitempty"` Enabled bool `json:"enabled"` UpdatedAt time.Time `json:"updated_at,omitempty"` } // HomeToolsRepo persists homepage grid tools. type HomeToolsRepo struct { Pool *pgxpool.Pool } // ListAll returns all tools ordered by row then sort. func (r *HomeToolsRepo) ListAll(ctx context.Context) ([]HomeTool, error) { return r.query(ctx, false) } // ListEnabled returns enabled tools for C-end. func (r *HomeToolsRepo) ListEnabled(ctx context.Context) ([]HomeTool, error) { return r.query(ctx, true) } func (r *HomeToolsRepo) query(ctx context.Context, onlyEnabled bool) ([]HomeTool, error) { q := ` SELECT id, row_index, sort_order, path, icon, label, badge, badge_tone, enabled, updated_at FROM home_tools` if onlyEnabled { q += ` WHERE enabled = true` } q += ` ORDER BY row_index, sort_order, label` rows, err := r.Pool.Query(ctx, q) if err != nil { return nil, err } defer rows.Close() var out []HomeTool for rows.Next() { var t HomeTool if err := rows.Scan( &t.ID, &t.RowIndex, &t.SortOrder, &t.Path, &t.Icon, &t.Label, &t.Badge, &t.BadgeTone, &t.Enabled, &t.UpdatedAt, ); err != nil { return nil, err } out = append(out, t) } return out, rows.Err() } // ReplaceAll deletes all rows and inserts items in one transaction. func (r *HomeToolsRepo) ReplaceAll(ctx context.Context, items []HomeTool) error { return r.ReplaceAllWithAudit(ctx, items, uuid.Nil, nil) } // ReplaceAllWithAudit replaces tools and optionally writes admin_audit_logs in one tx. // If adminID is uuid.Nil, skips audit insert. func (r *HomeToolsRepo) ReplaceAllWithAudit( ctx context.Context, items []HomeTool, adminID uuid.UUID, meta json.RawMessage, ) error { tx, err := r.Pool.Begin(ctx) if err != nil { return err } defer tx.Rollback(ctx) if _, err := tx.Exec(ctx, `DELETE FROM home_tools`); err != nil { return err } for _, it := range items { id := it.ID if id == uuid.Nil { id = uuid.New() } if _, err := tx.Exec(ctx, ` INSERT INTO home_tools(id, row_index, sort_order, path, icon, label, badge, badge_tone, enabled, updated_at) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,now())`, id, it.RowIndex, it.SortOrder, it.Path, it.Icon, it.Label, it.Badge, it.BadgeTone, it.Enabled, ); err != nil { return err } } if adminID != uuid.Nil { if meta == nil { meta = json.RawMessage(`{}`) } if _, err := tx.Exec(ctx, ` INSERT INTO admin_audit_logs(admin_id, action, target_type, target_id, meta) VALUES ($1,'home_tools.replace','home_tools','all',$2)`, adminID, meta); err != nil { return err } } return tx.Commit(ctx) }