menu

package
v1.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 27, 2026 License: Apache-2.0 Imports: 14 Imported by: 0

Documentation

Overview

Package menu provides domain models and repository interfaces for menu management.

Index

Constants

View Source
const (
	LinkTypeEntity   = "entity"
	LinkTypeExternal = "external"
	LinkTypeArchive  = "archive"
	LinkTypeCustom   = "custom"
)

Link types for menu items.

View Source
const (
	LocationHeader  = "header"
	LocationFooter  = "footer"
	LocationSidebar = "sidebar"
)

Menu locations.

Variables

View Source
var (
	ErrMenuNotFound      = errors.New("menu not found")
	ErrMenuItemNotFound  = errors.New("menu item not found")
	ErrSlugAlreadyExists = errors.New("menu slug already exists")
	ErrInvalidLinkType   = errors.New("invalid link type")
	ErrInvalidLocation   = errors.New("invalid menu location")
	ErrEntityMissing     = errors.New("menu item links to an entity that does not exist")
	ErrCircularParent    = errors.New("circular parent reference")
)

Common errors.

Functions

func ValidLinkType

func ValidLinkType(lt string) bool

ValidLinkType checks if a link type is valid.

func ValidLocation

func ValidLocation(loc string) bool

ValidLocation checks if a menu location is valid.

Types

type CreateMenuInput

type CreateMenuInput struct {
	Name     string  `json:"name"`
	Slug     string  `json:"slug,omitempty"`
	Location *string `json:"location,omitempty"`
}

CreateMenuInput represents input for creating a new menu.

type CreateMenuItemInput

type CreateMenuItemInput struct {
	ParentID     *uuid.UUID `json:"parent_id,omitempty"`
	Label        string     `json:"label"`
	LinkType     string     `json:"link_type"`
	EntityID     *uuid.UUID `json:"entity_id,omitempty"`
	URL          *string    `json:"url,omitempty"`
	SchemaSlug   *string    `json:"schema_slug,omitempty"`
	Position     *int       `json:"position,omitempty"`
	OpenInNewTab bool       `json:"open_in_new_tab"`
	CSSClasses   *string    `json:"css_classes,omitempty"`
}

CreateMenuItemInput represents input for creating a menu item.

type Menu struct {
	ID        uuid.UUID   `json:"id"`
	Name      string      `json:"name"`
	Slug      string      `json:"slug"`
	Location  *string     `json:"location,omitempty"`
	Items     []*MenuItem `json:"items,omitempty"`
	CreatedAt time.Time   `json:"created_at"`
	UpdatedAt time.Time   `json:"updated_at"`
}

Menu represents a navigation menu.

type MenuItem struct {
	ID           uuid.UUID   `json:"id"`
	MenuID       uuid.UUID   `json:"menu_id"`
	ParentID     *uuid.UUID  `json:"parent_id,omitempty"`
	Label        string      `json:"label"`
	LinkType     string      `json:"link_type"` // entity, external, archive, custom
	EntityID     *uuid.UUID  `json:"entity_id,omitempty"`
	URL          *string     `json:"url,omitempty"`
	SchemaSlug   *string     `json:"schema_slug,omitempty"`
	Position     int         `json:"position"`
	OpenInNewTab bool        `json:"open_in_new_tab"`
	CSSClasses   *string     `json:"css_classes,omitempty"`
	Children     []*MenuItem `json:"children,omitempty"`
	CreatedAt    time.Time   `json:"created_at"`
	UpdatedAt    time.Time   `json:"updated_at"`

	// Computed from entity (not stored in DB)
	EntitySlug *string `json:"entity_slug,omitempty"`
	EntityName *string `json:"entity_name,omitempty"`
	// EntitySchemaSlug is the linked entity's schema (render-side URL
	// building); hidden from JSON so the API shape is unchanged.
	EntitySchemaSlug *string `json:"-"`
}

MenuItem represents a single item in a menu with optional nesting.

type PostgresRepository

type PostgresRepository struct {
	// contains filtered or unexported fields
}

PostgresRepository implements Repository using PostgreSQL.

func NewPostgresRepository

func NewPostgresRepository(db *database.DB) *PostgresRepository

NewPostgresRepository creates a new PostgreSQL menu repository.

func (*PostgresRepository) CreateMenu

func (r *PostgresRepository) CreateMenu(ctx context.Context, input *CreateMenuInput) (*Menu, error)

CreateMenu inserts a new menu.

func (*PostgresRepository) CreateMenuItem

func (r *PostgresRepository) CreateMenuItem(ctx context.Context, menuID uuid.UUID, input *CreateMenuItemInput) (*MenuItem, error)

CreateMenuItem inserts a new menu item.

func (*PostgresRepository) DeleteMenu

func (r *PostgresRepository) DeleteMenu(ctx context.Context, id uuid.UUID) error

DeleteMenu removes a menu and all its items.

func (*PostgresRepository) DeleteMenuItem

func (r *PostgresRepository) DeleteMenuItem(ctx context.Context, id uuid.UUID) error

DeleteMenuItem removes a menu item and reassigns children to parent.

func (*PostgresRepository) GetMenuByID

func (r *PostgresRepository) GetMenuByID(ctx context.Context, id uuid.UUID) (*Menu, error)

GetMenuByID retrieves a menu by ID.

func (*PostgresRepository) GetMenuByLocation

func (r *PostgresRepository) GetMenuByLocation(ctx context.Context, location string) (*Menu, error)

GetMenuByLocation retrieves a menu by location.

func (*PostgresRepository) GetMenuBySlug

func (r *PostgresRepository) GetMenuBySlug(ctx context.Context, slug string) (*Menu, error)

GetMenuBySlug retrieves a menu by slug.

func (*PostgresRepository) GetMenuItemByID

func (r *PostgresRepository) GetMenuItemByID(ctx context.Context, id uuid.UUID) (*MenuItem, error)

GetMenuItemByID retrieves a menu item by ID.

func (*PostgresRepository) GetMenuWithItems

func (r *PostgresRepository) GetMenuWithItems(ctx context.Context, menuID uuid.UUID) (*Menu, error)

GetMenuWithItems retrieves a menu with all items in nested structure.

func (*PostgresRepository) ListMenus

func (r *PostgresRepository) ListMenus(ctx context.Context) ([]*Menu, error)

ListMenus retrieves all menus for the tenant.

func (*PostgresRepository) ReorderItems

func (r *PostgresRepository) ReorderItems(ctx context.Context, menuID uuid.UUID, items []ReorderInput) error

ReorderItems updates positions and parent relationships for multiple items.

func (*PostgresRepository) ReplaceMenu

func (r *PostgresRepository) ReplaceMenu(ctx context.Context, id uuid.UUID, fields *UpdateMenuInput, items []ReplaceItem) error

ReplaceMenu implements Repository. The menu row is locked for the whole call, so concurrent replacements run one after the other.

func (*PostgresRepository) UpdateMenu

func (r *PostgresRepository) UpdateMenu(ctx context.Context, id uuid.UUID, input *UpdateMenuInput) (*Menu, error)

UpdateMenu modifies a menu.

func (*PostgresRepository) UpdateMenuItem

func (r *PostgresRepository) UpdateMenuItem(ctx context.Context, id uuid.UUID, input *UpdateMenuItemInput) (*MenuItem, error)

UpdateMenuItem modifies a menu item.

type ReorderInput

type ReorderInput struct {
	ID       uuid.UUID  `json:"id"`
	ParentID *uuid.UUID `json:"parent_id,omitempty"`
	Position int        `json:"position"`
}

ReorderInput represents a single item in a reorder request.

type ReplaceItem

type ReplaceItem struct {
	Label        string
	LinkType     string
	EntityID     *uuid.UUID
	URL          *string
	SchemaSlug   *string
	OpenInNewTab bool
	Parent       int
}

ReplaceItem is one item of a ReplaceMenu call. Parent is the index of an earlier item in the same list, or -1 for the top level.

type Repository

type Repository interface {
	// Menu operations
	CreateMenu(ctx context.Context, input *CreateMenuInput) (*Menu, error)
	GetMenuByID(ctx context.Context, id uuid.UUID) (*Menu, error)
	GetMenuBySlug(ctx context.Context, slug string) (*Menu, error)
	GetMenuByLocation(ctx context.Context, location string) (*Menu, error)
	ListMenus(ctx context.Context) ([]*Menu, error)
	UpdateMenu(ctx context.Context, id uuid.UUID, input *UpdateMenuInput) (*Menu, error)
	DeleteMenu(ctx context.Context, id uuid.UUID) error

	// Menu item operations
	CreateMenuItem(ctx context.Context, menuID uuid.UUID, input *CreateMenuItemInput) (*MenuItem, error)
	GetMenuItemByID(ctx context.Context, id uuid.UUID) (*MenuItem, error)
	UpdateMenuItem(ctx context.Context, id uuid.UUID, input *UpdateMenuItemInput) (*MenuItem, error)
	DeleteMenuItem(ctx context.Context, id uuid.UUID) error

	// Nested operations
	GetMenuWithItems(ctx context.Context, menuID uuid.UUID) (*Menu, error)
	ReorderItems(ctx context.Context, menuID uuid.UUID, items []ReorderInput) error

	// ReplaceMenu applies the menu fields (when set) and replaces every item
	// in one transaction: either all of it happens or none of it.
	ReplaceMenu(ctx context.Context, id uuid.UUID, fields *UpdateMenuInput, items []ReplaceItem) error
}

Repository defines the interface for menu data operations.

type Service

type Service struct {
	// contains filtered or unexported fields
}

Service provides business logic for menu management.

func NewService

func NewService(repo Repository, logger *slog.Logger) *Service

NewService creates a new menu service.

func (*Service) CreateMenu

func (s *Service) CreateMenu(ctx context.Context, input *CreateMenuInput) (*Menu, error)

CreateMenu creates a new menu.

func (*Service) CreateMenuItem

func (s *Service) CreateMenuItem(ctx context.Context, menuID uuid.UUID, input *CreateMenuItemInput) (*MenuItem, error)

CreateMenuItem adds an item to a menu.

func (*Service) DeleteMenu

func (s *Service) DeleteMenu(ctx context.Context, id uuid.UUID) error

DeleteMenu removes a menu.

func (*Service) DeleteMenuItem

func (s *Service) DeleteMenuItem(ctx context.Context, id uuid.UUID) error

DeleteMenuItem removes a menu item.

func (*Service) GetMenu

func (s *Service) GetMenu(ctx context.Context, id uuid.UUID) (*Menu, error)

GetMenu retrieves a menu by ID.

func (*Service) GetMenuByLocation

func (s *Service) GetMenuByLocation(ctx context.Context, location string) (*Menu, error)

GetMenuByLocation retrieves a menu by location.

func (*Service) GetMenuBySlug

func (s *Service) GetMenuBySlug(ctx context.Context, slug string) (*Menu, error)

GetMenuBySlug retrieves a menu by slug.

func (*Service) GetMenuItem

func (s *Service) GetMenuItem(ctx context.Context, id uuid.UUID) (*MenuItem, error)

GetMenuItem retrieves a menu item by ID.

func (*Service) GetMenuWithItems

func (s *Service) GetMenuWithItems(ctx context.Context, menuID uuid.UUID) (*Menu, error)

GetMenuWithItems retrieves a menu with all nested items.

func (*Service) ListMenus

func (s *Service) ListMenus(ctx context.Context) ([]*Menu, error)

ListMenus retrieves all menus for the tenant.

func (*Service) ReorderItems

func (s *Service) ReorderItems(ctx context.Context, menuID uuid.UUID, items []ReorderInput) error

ReorderItems updates positions and parent relationships for multiple items.

func (*Service) ReplaceMenu

func (s *Service) ReplaceMenu(ctx context.Context, id uuid.UUID, fields *UpdateMenuInput, items []ReplaceItem) (*Menu, error)

ReplaceMenu updates a menu's name/location and replaces its items in one transaction. A nil items list leaves the items alone.

func (*Service) SetChangeObserver

func (s *Service) SetChangeObserver(f func(ctx context.Context))

SetChangeObserver installs the callback run after a menu or menu item was written; nil disables it.

func (*Service) UpdateMenu

func (s *Service) UpdateMenu(ctx context.Context, id uuid.UUID, input *UpdateMenuInput) (*Menu, error)

UpdateMenu modifies a menu.

func (*Service) UpdateMenuItem

func (s *Service) UpdateMenuItem(ctx context.Context, id uuid.UUID, input *UpdateMenuItemInput) (*MenuItem, error)

UpdateMenuItem modifies a menu item.

type UpdateMenuInput

type UpdateMenuInput struct {
	Name     *string `json:"name,omitempty"`
	Location *string `json:"location,omitempty"`
}

UpdateMenuInput represents input for updating a menu.

type UpdateMenuItemInput

type UpdateMenuItemInput struct {
	ParentID     *uuid.UUID `json:"parent_id,omitempty"`
	Label        *string    `json:"label,omitempty"`
	LinkType     *string    `json:"link_type,omitempty"`
	EntityID     *uuid.UUID `json:"entity_id,omitempty"`
	URL          *string    `json:"url,omitempty"`
	SchemaSlug   *string    `json:"schema_slug,omitempty"`
	Position     *int       `json:"position,omitempty"`
	OpenInNewTab *bool      `json:"open_in_new_tab,omitempty"`
	CSSClasses   *string    `json:"css_classes,omitempty"`
}

UpdateMenuItemInput represents input for updating a menu item.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL