// Copyright (c) 2024 Zeni Kim // Use of this source code is governed by MIT-style // license that can be found in the LICENSE file. package controllers import ( "fmt" "math/rand/v2" "os" "strconv" "time" "git.smarteching.com/goffee/core" "git.smarteching.com/goffee/core/template/components" ) // Show home page func Themedemo(c *core.Context) *core.Response { // check if template engine is enabled TemplateEnableStr := os.Getenv("TEMPLATE_ENABLE") if TemplateEnableStr == "" { TemplateEnableStr = "false" } TemplateEnable, _ := strconv.ParseBool(TemplateEnableStr) if TemplateEnable { // first, include all compoments type templateData struct { PageCard components.PageCard } // now fill data of the components tmplData := templateData{ PageCard: components.PageCard{ CardTitle: "Card title", CardBody: "Loerm ipsum at deim", }, } return c.Response.Template("custom_theme_base.html", tmplData) } else { message := "{\"message\": \"Error, template not enabled\"}" return c.Response.Json(message) } } // Show form element page func Themeform(c *core.Context) *core.Response { // check if template engine is enabled TemplateEnableStr := os.Getenv("TEMPLATE_ENABLE") if TemplateEnableStr == "" { TemplateEnableStr = "false" } TemplateEnable, _ := strconv.ParseBool(TemplateEnableStr) if TemplateEnable { // first, include all compoments type templateData struct { FormText components.FormInput FormEmail components.FormInput FormButton components.FormButton FormSelectCity components.FormSelect FormTextarea components.FormTextarea FormRadio components.FormRadio FormCheckbox components.FormCheckbox } // for select options var allOptions []components.FormSelectOption var option components.FormSelectOption option.Value = "ch" option.Caption = "China" allOptions = append(allOptions, option) option.Value = "ba" option.Caption = "Buenos Aires" allOptions = append(allOptions, option) option.Value = "fr" option.Caption = "France" selectedOption := option allOptions = append(allOptions, option) // for radio options var allOptionsr []components.FormRadioItem var optionr components.FormRadioItem optionr.ID = "citysch" optionr.Name = "citys" optionr.Value = "china" optionr.Label = "China" allOptionsr = append(allOptionsr, optionr) optionr.ID = "citysba" optionr.Name = "citys" optionr.Value = "buenosaires" optionr.Label = "Buenos Aires" //optionr.IsDisabled = true allOptionsr = append(allOptionsr, optionr) // for radio options var allOptionsc []components.FormCheckboxItem var optionc components.FormCheckboxItem optionc.ID = "citysch" optionc.Name = "citys" optionc.Value = "china" optionc.Label = "China" allOptionsc = append(allOptionsc, optionc) optionc.ID = "citysba" optionc.Name = "citys" optionc.Value = "buenosaires" optionc.Label = "Buenos Aires" allOptionsc = append(allOptionsc, optionc) optionc.ID = "london" optionc.Name = "london" optionc.Value = "london" optionc.Label = "London" //optionc.IsChecked = true allOptionsc = append(allOptionsc, optionc) // now fill data of the components tmplData := templateData{ FormText: components.FormInput{ ID: "text", Label: "Name", Type: "text", Hint: "This is sample hint", Placeholder: "Enter your name", }, FormEmail: components.FormInput{ ID: "email", Label: "Email", Type: "email", IsRequired: true, Placeholder: "Enter your email address", }, FormButton: components.FormButton{ Text: "Login", IsSubmit: true, TypeClass: "primary", }, FormSelectCity: components.FormSelect{ ID: "city", Label: "Select city", AllOptions: allOptions, SelectedOption: selectedOption, }, FormTextarea: components.FormTextarea{ ID: "text", Label: "Example textarea", }, FormRadio: components.FormRadio{ Label: "Radio buttons", AllRadios: allOptionsr, }, FormCheckbox: components.FormCheckbox{ Label: "Checkbox options", AllCheckbox: allOptionsc, }, } return c.Response.Template("custom_theme_formpage.html", tmplData) } else { message := "{\"message\": \"Error, template not enabled\"}" return c.Response.Json(message) } } func ThemeElements(c *core.Context) *core.Response { // check if template engine is enabled TemplateEnableStr := os.Getenv("TEMPLATE_ENABLE") if TemplateEnableStr == "" { TemplateEnableStr = "false" } TemplateEnable, _ := strconv.ParseBool(TemplateEnableStr) if TemplateEnable { type templateData struct { Buttons []components.FormButton Hrefs []components.ContentHref Badges []components.ContentBadge Dropdowns []components.ContentDropdown Lists []components.ContentList Menus []components.PageNav } buttons := []components.FormButton{ { Text: "primary", TypeClass: "primary", }, { Text: "secondary", TypeClass: "secondary", }, { Text: "success", TypeClass: "success", }, { Text: "danger", TypeClass: "danger", }, { Text: "warning", TypeClass: "warning", }, { Text: "info", TypeClass: "info", }, { Text: "light", TypeClass: "light", }, { Text: "dark", TypeClass: "dark", }, { Text: "link", TypeClass: "link", }, { Text: "disabled", TypeClass: "primary", IsDisabled: true, }, { Text: "outline-primary", TypeClass: "outline-primary", }, { Text: "outline-secondary", TypeClass: "outline-secondary", }, { Text: "outline-success", TypeClass: "outline-success", }, { Text: "outline-danger", TypeClass: "outline-danger", }, { Text: "outline-warning", TypeClass: "outline-warning", }, { Text: "outline-info", TypeClass: "outline-info", }, { Text: "outline-light", TypeClass: "outline-light", }, { Text: "outline-dark", TypeClass: "outline-dark", }, } hrefs := []components.ContentHref{ { Text: "href", Link: "#", IsButton: false, }, { Text: "link", Link: "#", IsButton: false, TypeClass: "link", }, { Text: "button", Link: "#", IsButton: true, TypeClass: "primary", }, { Text: "href disabled", Link: "#", IsButton: false, IsDisabled: true, }, { Text: "link disabled", Link: "#", TypeClass: "link", IsDisabled: true, }, { Text: "button disabled", Link: "#", IsButton: true, TypeClass: "primary", IsDisabled: true, }, } badges := []components.ContentBadge{ { Text: "primary", TypeClass: "primary", }, { Text: "secondary", TypeClass: "secondary", }, { Text: "success", TypeClass: "success", }, { Text: "danger", TypeClass: "danger", }, { Text: "warning", TypeClass: "warning", }, { Text: "info", TypeClass: "info", }, { Text: "light", TypeClass: "light", }, { Text: "dark", TypeClass: "dark", }, { Text: "outline", TypeClass: "primary", IsOutline: true, }, { Text: "outline", TypeClass: "success", IsOutline: true, }, { Text: "outline", TypeClass: "danger", IsOutline: true, }, { Text: "outline", TypeClass: "warning", IsOutline: true, }, } dropdowns := []components.ContentDropdown{ // dropdown { Label: "dropdown", Items: []components.ContentDropdownItem{ { Text: "item ", Link: "#", }, { Text: "item disabled", Link: "#", IsDisabled: true, }, }, }, // dropdown { Label: "primary", TypeClass: "primary", Items: []components.ContentDropdownItem{ { Text: "item ", Link: "#", }, { Text: "item ", Link: "#", IsActive: true, }, { Text: "item disabled", Link: "#", IsDisabled: true, }, }, }, // dropdown { Label: "outline", TypeClass: "outline-primary", Items: []components.ContentDropdownItem{ { Text: "item ", Link: "#", }, }, }, // dropdown { Label: "disabled", TypeClass: "primary", IsDisabled: true, // items }, } list := []components.ContentList{ // basic list { Items: []components.ContentListItem{ { Text: "item 1", }, { Text: "item 2", EndElement: "end text", }, { Text: "item disabled", IsDisabled: true, }, }, }, // description list { Items: []components.ContentListItem{ { Text: "item 1", Description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. ", }, { Text: "item 2", Description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", }, { Text: "item disabled", Description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", IsDisabled: true, }, }, }, // list with class { Items: []components.ContentListItem{ { Text: "class primary", TypeClass: "primary", }, { Text: "class success", TypeClass: "success", }, { Text: "class danger", TypeClass: "danger", }, }, }, } menus := []components.PageNav{ // nav { NavClass: "nav-pills", NavItems: []components.PageNavItem{ { Text: "item active", Link: "#", IsActive: true, }, { Text: "item", Link: "#", IsActive: false, }, { Text: "item", Link: "#", IsActive: false, ChildItems: []components.PageNavItem{ { Text: "item ", Link: "#", }, { Text: "item disabled", Link: "#", IsDisabled: true, }, }, }, { Text: "item disabled", Link: "#", IsDisabled: true, }, }, }, // nav { NavClass: "", NavItems: []components.PageNavItem{ { Text: "item active", Link: "#", IsActive: true, }, { Text: "item", Link: "#", IsActive: false, }, { Text: "item", Link: "#", IsActive: false, }, { Text: "item disabled", Link: "#", IsDisabled: true, }, }, }, // nav underline { NavClass: "nav-underline", NavItems: []components.PageNavItem{ { Text: "item active", Link: "#", IsActive: true, }, { Text: "item", Link: "#", IsActive: false, }, { Text: "item", Link: "#", IsActive: false, }, { Text: "item disabled", Link: "#", IsDisabled: true, }, }, }, // nav tabs { NavClass: "", IsTab: true, NavItems: []components.PageNavItem{ { Text: "tab active", Link: "#", IsActive: true, }, { Text: "tab", Link: "#", IsActive: false, }, { Text: "tab", Link: "#", IsActive: false, }, { Text: "tab disabled", Link: "#", IsDisabled: true, }, }, }, // nav vertical { NavClass: "", IsVertical: true, NavItems: []components.PageNavItem{ { Text: "item active", Link: "#", IsActive: true, }, { Text: "item", Link: "#", IsActive: false, }, { Text: "item", Link: "#", IsActive: false, }, { Text: "item disabled", Link: "#", IsDisabled: true, }, }, }, } tmplData := templateData{ Buttons: buttons, Hrefs: hrefs, Badges: badges, Dropdowns: dropdowns, Lists: list, Menus: menus, } return c.Response.Template("custom_theme_elements.html", tmplData) } else { message := "{\"message\": \"Error, template not enabled\"}" return c.Response.Json(message) } } // Show form element page func Themecontent(c *core.Context) *core.Response { // check if template engine is enabled TemplateEnableStr := os.Getenv("TEMPLATE_ENABLE") if TemplateEnableStr == "" { TemplateEnableStr = "false" } TemplateEnable, _ := strconv.ParseBool(TemplateEnableStr) if TemplateEnable { // first, include all compoments type templateData struct { ContentTable components.ContentTable ContentTabledetail components.ContentTabledetail ContentGraph components.ContentGraph FieldText components.FormInput FormSelectCityM components.FormSelect Pagination components.ContentPagination ShouldShowPagination bool } // for select options var allOptions []components.FormSelectOption var option components.FormSelectOption option.Value = "ch" option.Caption = "China" allOptions = append(allOptions, option) option.Value = "ba" option.Caption = "Buenos Aires" allOptions = append(allOptions, option) option.Value = "fr" option.Caption = "France" selectedOption := option allOptions = append(allOptions, option) option.Value = "kr" option.Caption = "Korea" allOptions = append(allOptions, option) // for custom attributes in form var customAtt []components.CustomAtt var attribute components.CustomAtt attribute.AttName = "code" attribute.AttValue = "five" customAtt = append(customAtt, attribute) attribute.AttName = "mytag" attribute.AttValue = "My value" customAtt = append(customAtt, attribute) // TABLES // for th head var allTh []components.ContentTableTH var th components.ContentTableTH th.Value = "Column heading 1" allTh = append(allTh, th) th.Value = "Column heading 2" allTh = append(allTh, th) th.ID = "ba" th.Value = "Column heading 3" allTh = append(allTh, th) th.Value = "Column badge" th.ValueType = "badge" // column type badge allTh = append(allTh, th) th.Value = "Column action" th.ValueType = "href" // column type href allTh = append(allTh, th) // for td items var allTd [][]components.ContentTableTD //var vals []components.ContentTableTD // rows for i := 1; i <= 28; i++ { vals := make([]components.ContentTableTD, len(allTh)) for b := 0; b < len(allTh)-2; b++ { vals[b].Value = fmt.Sprintf("%s%d%d", "TD data: ", i, b) vals[b].ID = fmt.Sprintf("%s%d%d", "idtd_", i, b) } // column badge vals[len(allTh)-2].Value = components.ContentBadge{ Text: "success", TypeClass: "success", } // last column href vals[len(allTh)-1].Value = components.ContentHref{ Text: "edit", Link: "#", } allTd = append(allTd, vals) } // Pagination demo // start config limit := 10 shouldShowPagination := false pageViewTableOffset := 0 // Get the length of AllOptions totalrecords := len(allTd) // get current table offset tpage := c.RequestParamExists("tpage") if tpage { pageViewTableOffset, _ = strconv.Atoi(c.GetRequestParam("tpage").(string)) } // start default option paginator var pagination components.ContentPagination pagination.PageStartRecord = pageViewTableOffset + 1 pagination.PageEndRecord = 0 pagination.TotalRecords = 0 pagination.PrevLink = "" pagination.NextLink = "" // check current page // fake function to emulate a query offset newTd := getPaginatedPageViews(allTd, limit, pageViewTableOffset) if len(newTd) > 0 { pagination.TotalRecords = totalrecords pagination.PageStartRecord = pageViewTableOffset + 1 pagination.PageEndRecord = pageViewTableOffset + len(newTd) shouldShowPagination = totalrecords > limit } if shouldShowPagination && pageViewTableOffset != 0 { pagination.PrevLink = fmt.Sprintf("/themecontent?tpage=%d", pageViewTableOffset-limit) } if shouldShowPagination && pageViewTableOffset+limit < totalrecords { pagination.NextLink = fmt.Sprintf("/themecontent?tpage=%d", pageViewTableOffset+limit) } // for td items in table detail var allTdetail []components.ContentTabledetailTD // table detail var thd components.ContentTabledetailTD thd.Caption = "Continent" thd.Value = "Asia" allTdetail = append(allTdetail, thd) thd.Caption = "Country" thd.Value = "South Korea" allTdetail = append(allTdetail, thd) thd.Caption = "Capital" thd.Value = "Seoul" allTdetail = append(allTdetail, thd) thd.Caption = "Details" thd.ValueType = "href" // column type href thd.Value = components.ContentHref{ Text: "edit", Link: "#", } allTdetail = append(allTdetail, thd) thd.Caption = "Notifications" thd.ValueType = "badge" // column type href thd.Value = components.ContentBadge{ Text: "success", TypeClass: "success", } allTdetail = append(allTdetail, thd) // random values for pie one := rand.IntN(50) two := rand.IntN(50) three := rand.IntN(50) valuesgraph := fmt.Sprintf("%d|%d|%d", one, two, three) // now fill data of the components tmplData := templateData{ FormSelectCityM: components.FormSelect{ ID: "city", Label: "Select city", AllOptions: allOptions, SelectedOption: selectedOption, IsMultiple: true, }, FieldText: components.FormInput{ ID: "text", Label: "Name", Type: "text", Hint: "This is sample hint", Placeholder: "Enter your name", CustomAtt: customAtt, }, ContentTable: components.ContentTable{ ID: "table_demo", AllTH: allTh, AllTD: newTd, }, ContentTabledetail: components.ContentTabledetail{ ID: "table_demodetail", Title: "Sample table detail", HeadClass: "table-warning", AllTD: allTdetail, }, ContentGraph: components.ContentGraph{ Graph: "pie", Labels: "Berlin|Paris|Venecia", Values: valuesgraph, }, Pagination: pagination, ShouldShowPagination: shouldShowPagination, } return c.Response.Template("custom_theme_contentpage.html", tmplData) } else { message := "{\"message\": \"Error, template not enabled\"}" return c.Response.Json(message) } } func getPaginatedPageViews(values [][]components.ContentTableTD, limit int, offset int) [][]components.ContentTableTD { // Validate the offset and adjust if necessary if offset < 0 { offset = 0 // Ensure offset is not negative } else if offset >= len(values) { var emptytd [][]components.ContentTableTD return emptytd } // Calculate the end index (limit the slice to the size of the array) end := offset + limit if end > len(values) { end = len(values) // Ensure end doesn't exceed the length of the array } return values[offset:end] // Slice the array } // Custom Templates functions func TemplatesFunctions(c *core.Context) *core.Response { // check if template engine is enabled TemplateEnableStr := os.Getenv("TEMPLATE_ENABLE") if TemplateEnableStr == "" { TemplateEnableStr = "false" } TemplateEnable, _ := strconv.ParseBool(TemplateEnableStr) if TemplateEnable { tmplData := SamplePageData() return c.Response.Template("custom_templates_functions.html", tmplData) } else { message := "{\"message\": \"Error, template not enabled\"}" return c.Response.Json(message) } } // Author represents the writer of an article to show the custom functions type Author struct { Name string Avatar string Bio string } // Article represents a blog post or news entry used to test template helpers to show the custom functions type Article struct { Title string Slug string Excerpt string Body string Tags []string PublishedAt time.Time UpdatedAt time.Time Author Author Views int Price float64 Featured bool Subtitle string // intentionally left empty on some entries to test coalesce/defaultVal } // PageData is the top-level context passed to the HTML template to show the custom functions type PageData struct { SiteTitle string Articles []Article } // SamplePageData returns a populated PageData ready to be rendered by the template. func SamplePageData() PageData { return PageData{ SiteTitle: "go/template lab", Articles: []Article{ { Title: "getting started with go templates", Slug: "getting-started-go-templates", Excerpt: "Go's html/template package is both powerful and safe by default. In this article we explore how to extend it with custom FuncMap helpers that bring it closer to the expressiveness of Liquid or Twig, without sacrificing any of the security guarantees.", Tags: []string{"go", "templates", "web", "backend"}, PublishedAt: time.Now().Add(-3 * 24 * time.Hour), UpdatedAt: time.Now().Add(-1 * 24 * time.Hour), Views: 14200, Price: 0, Featured: true, Subtitle: "", Author: Author{ Name: "marina voss", Avatar: "MV", Bio: "Senior backend engineer focused on developer tooling and observability.", }, }, { Title: "building a blog engine in go", Slug: "blog-engine-go", Excerpt: "We walk through building a minimal but complete blog engine using only the Go standard library: routing with net/http, persistence with database/sql, and rendering with html/template.", Tags: []string{"go", "blog", "sqlite"}, PublishedAt: time.Now().Add(-10 * 24 * time.Hour), UpdatedAt: time.Now().Add(-10 * 24 * time.Hour), Views: 8750, Price: 9.99, Featured: false, Subtitle: "A zero-dependency approach", Author: Author{ Name: "rafael okonkwo", Avatar: "RO", Bio: "Full-stack engineer and open-source contributor. Writes about Go and distributed systems.", }, }, { Title: "concurrency patterns you should know", Slug: "concurrency-patterns-go", Excerpt: "Goroutines are cheap, but misusing them is expensive. This deep-dive covers fan-out/fan-in, pipelines, semaphores, and error group patterns with real production examples.", Tags: []string{"go", "concurrency", "goroutines", "advanced"}, PublishedAt: time.Now().Add(-45 * 24 * time.Hour), UpdatedAt: time.Now().Add(-40 * 24 * time.Hour), Views: 31400, Price: 14.99, Featured: true, Subtitle: "", Author: Author{ Name: "selin çelik", Avatar: "SÇ", Bio: "Systems programmer. Previously at Cloudflare. Loves writing about the internals of things.", }, }, { Title: "understanding go interfaces", Slug: "understanding-go-interfaces", Excerpt: "Interfaces in Go are implicit and structural, which makes them both elegant and occasionally surprising. We look at how the runtime dispatches method calls and how to design composable interfaces.", Tags: []string{"go", "interfaces", "design"}, PublishedAt: time.Now().Add(-2 * time.Hour), UpdatedAt: time.Now().Add(-1 * time.Hour), Views: 420, Price: 0, Featured: false, Subtitle: "Implicit, structural, and powerful", Author: Author{ Name: "marina voss", Avatar: "MV", Bio: "Senior backend engineer focused on developer tooling and observability.", }, }, }, } }