HTML Views

Overview

HTML Views are responsible for generating complete HTML pages by assembling layouts, sections, and components. They coordinate the presentation layer, bringing together data from Maestro operations and formatting it into structured HTML responses.

Purpose

HTML Views serve several key functions: - Page Assembly - Combine layouts, sections, and components into complete pages - Data Presentation - Transform Maestro output into HTML structures - Layout Management - Apply consistent page layouts across the application - Component Orchestration - Coordinate multiple UI components

Structure

Each HTML view typically follows this pattern:

type ListPlantTasksView struct {
    Kit           *kit.Kit
    ListSection   *sections.ListSection
    NavbarSection *sections.NavbarSection
    TiledLayout   *layouts.TiledLayout
    TitleSection  *sections.TitleSection
}

func (this *ListPlantTasksView) H(out *maes.ListPlantTasksMaeOut) goc.HTML {
    mod := &layouts.LayoutMod{
        Messages: out.Extra.Messages,
        Scope:    out.Extra.Scope,
        Ctx:      out.Ctx,
    }
    return this.TiledLayout.H(this.tiling(out), mod)
}

Core Methods

Main Render Method

func (this *SomeView) H(out *maes.SomeMaeOut) goc.HTML {
    mod := &layouts.LayoutMod{
        Messages: out.Extra.Messages,
        Scope:    out.Extra.Scope,
        Ctx:      out.Ctx,
        Title:    "Page Title",
    }

    return this.StructuredLayout.H(this.content(out), mod)
}

Content Assembly

func (this *ListPlantTasksView) tiling(out *maes.ListPlantTasksMaeOut) goc.HTML {
    component := this.Kit.Component

    return component.Dcs("flex flex-col space-y-4",
        this.NavbarSection.H(out),
        this.TitleSection.H(out),
        this.ListSection.H(out),
        this.footerSection(out),
    )
}

Layout Integration

Layout Types

// Structured layout with sidebar
func (this *SomeView) H(out *maes.SomeMaeOut) goc.HTML {
    return this.StructuredLayout.H(content, mod)
}

// Tiled layout for flexible content
func (this *SomeView) H(out *maes.SomeMaeOut) goc.HTML {
    return this.TiledLayout.H(content, mod)
}

// Vertical layout for forms
func (this *SomeView) H(out *maes.SomeMaeOut) goc.HTML {
    return this.VerticalLayout.H(content, mod)
}

// Empty layout for minimal pages
func (this *SomeView) H(out *maes.SomeMaeOut) goc.HTML {
    return this.EmptyLayout.H(content, mod)
}

Layout Modification

func (this *SomeView) H(out *maes.SomeMaeOut) goc.HTML {
    mod := &layouts.LayoutMod{
        Messages:     out.Extra.Messages,
        Scope:        out.Extra.Scope,
        Ctx:          out.Ctx,
        Title:        "Custom Page Title",
        MetaTitle:    "SEO Title",
        Description:  "Page description for SEO",
        Keywords:     []string{"keyword1", "keyword2"},
        BodyClasses:  []string{"custom-page", "special-layout"},
    }

    return this.StructuredLayout.H(this.content(out), mod)
}

Section Integration

Common Sections

func (this *SomeView) content(out *maes.SomeMaeOut) goc.HTML {
    return this.Kit.Component.Dcs("container mx-auto",
        // Navigation
        this.NavbarSection.H(out),

        // Page title
        this.TitleSection.H(out),

        // Main content
        this.mainContent(out),

        // Actions
        this.ActionSection.H(out),
    )
}

Custom Sections

func (this *SomeView) customSection(out *maes.SomeMaeOut) goc.HTML {
    component := this.Kit.Component

    return component.Dcs("bg-gray-100 p-4 rounded",
        component.H2("Custom Section"),
        component.P("This is custom content specific to this view"),
        this.customData(out.Data),
    )
}

Component Usage

Form Components

func (this *CreatePlantView) formSection(out *maes.CreatePlantMaeOut) goc.HTML {
    return this.Kit.Component.Form(
        this.Kit.Component.FormField("text", "name", "Plant Name", out.Form.Name),
        this.Kit.Component.FormField("text", "species", "Species", out.Form.Species),
        this.Kit.Component.FormField("number", "size", "Size", out.Form.Size),
        this.Kit.Component.FormField("checkbox", "perennial", "Perennial", out.Form.Perennial),
        this.Kit.Component.SubmitButton("Create Plant"),
    )
}

Table Components

func (this *ListPlantsView) tableSection(out *maes.ListPlantsMaeOut) goc.HTML {
    var rows []goc.HTML

    for _, plant := range out.Plants {
        row := this.Kit.Component.TableRow(
            this.Kit.Component.TableCell(plant.Name),
            this.Kit.Component.TableCell(plant.Species),
            this.Kit.Component.TableCell(fmt.Sprintf("%d", plant.Size)),
            this.actionsCell(plant),
        )
        rows = append(rows, row)
    }

    headers := []string{"Name", "Species", "Size", "Actions"}
    return this.Kit.Component.Table(headers, rows)
}
func (this *SomeView) navigationSection(out *maes.SomeMaeOut) goc.HTML {
    return this.Kit.Component.Nav(
        this.Kit.Component.NavLink("/plants", "Plants"),
        this.Kit.Component.NavLink("/gardens", "Gardens"),
        this.Kit.Component.NavLink("/tasks", "Tasks"),
        this.userMenu(out.User),
    )
}

Data Handling

Error Display

func (this *SomeView) H(out *maes.SomeMaeOut) goc.HTML {
    content := this.mainContent(out)

    // Add error messages if present
    if len(out.Extra.Messages.Errors) > 0 {
        content = this.Kit.Component.Dcs("space-y-4",
            this.errorMessages(out.Extra.Messages.Errors),
            content,
        )
    }

    return this.StructuredLayout.H(content, mod)
}

func (this *SomeView) errorMessages(errors []string) goc.HTML {
    var errorElements []goc.HTML

    for _, error := range errors {
        errorElements = append(errorElements,
            this.Kit.Component.Dcs("bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded",
                this.Kit.Component.Text(error),
            ),
        )
    }

    return this.Kit.Component.Dcs("space-y-2", errorElements...)
}

Success Messages

func (this *SomeView) successMessages(messages []string) goc.HTML {
    var messageElements []goc.HTML

    for _, message := range messages {
        messageElements = append(messageElements,
            this.Kit.Component.Dcs("bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded",
                this.Kit.Component.Text(message),
            ),
        )
    }

    return this.Kit.Component.Dcs("space-y-2", messageElements...)
}

Conditional Content

User-Based Content

func (this *SomeView) content(out *maes.SomeMaeOut) goc.HTML {
    content := this.mainContent(out)

    if out.User != nil && out.User.IsAdmin {
        content = this.Kit.Component.Dcs("space-y-4",
            content,
            this.adminPanel(out),
        )
    }

    return content
}

Data-Based Content

func (this *ListPlantsView) content(out *maes.ListPlantsMaeOut) goc.HTML {
    if len(out.Plants) == 0 {
        return this.emptyState()
    }

    return this.Kit.Component.Dcs("space-y-6",
        this.TitleSection.H(out),
        this.filtersSection(out),
        this.plantsTable(out),
        this.paginationSection(out),
    )
}

func (this *ListPlantsView) emptyState() goc.HTML {
    return this.Kit.Component.Dcs("text-center py-12",
        this.Kit.Component.H2("No plants found"),
        this.Kit.Component.P("Start by creating your first plant"),
        this.Kit.Component.Link("/plants/new", "Create Plant"),
    )
}

Integration with Controllers

HTML Views are typically used in controller methods:

func (gc *GardenController) List(c *gin.Context) {
    input := &maes.ListGardensMaeIn{}

    output, err := gc.ListGardensMae.Execute(c.Request.Context(), input)
    if err != nil {
        c.JSON(500, gin.H{"error": err.Error()})
        return
    }

    html := gc.ListGardensView.H(output)
    c.Data(200, "text/html; charset=utf-8", []byte(html))
}

Best Practices

Performance

  • Minimize component nesting depth
  • Reuse components across views
  • Avoid expensive operations in view methods

Maintainability

  • Keep views focused on presentation logic
  • Extract common patterns into reusable components
  • Use consistent naming conventions

Responsive Design

  • Use responsive CSS classes
  • Test views across different screen sizes
  • Implement mobile-first approaches

Accessibility

  • Include proper ARIA labels
  • Ensure keyboard navigation works
  • Provide alt text for images

Testing HTML Views

func TestListPlantsView_H(t *testing.T) {
    view := setupListPlantsView()

    output := &maes.ListPlantsMaeOut{
        Plants: []*mdl.Plant{
            {Name: "Tomato", Species: "Solanum lycopersicum"},
            {Name: "Basil", Species: "Ocimum basilicum"},
        },
    }

    html := view.H(output)

    assert.Contains(t, string(html), "Tomato")
    assert.Contains(t, string(html), "Basil")
    assert.Contains(t, string(html), "Solanum lycopersicum")
}

func TestCreatePlantView_ErrorHandling(t *testing.T) {
    view := setupCreatePlantView()

    output := &maes.CreatePlantMaeOut{
        Extra: &maes.MaeExtra{
            Messages: &maes.Messages{
                Errors: []string{"Name is required", "Size must be positive"},
            },
        },
    }

    html := view.H(output)

    assert.Contains(t, string(html), "Name is required")
    assert.Contains(t, string(html), "Size must be positive")
}

HTML Views provide the final layer in the presentation stack, assembling all UI components into cohesive, functional web pages that deliver great user experiences.