diff --git a/cmd/search/search.go b/cmd/search/search.go index c58148b..c86002d 100644 --- a/cmd/search/search.go +++ b/cmd/search/search.go @@ -43,7 +43,6 @@ type fuzzyResult struct { type resultBuffer struct { createdAt time.Time hits int - mu sync.Mutex results []fuzzyResult } @@ -56,6 +55,7 @@ var ( var searchTargets map[table.TechnicalName][]string = make(map[table.TechnicalName][]string) var resultsBuffer map[string]*resultBuffer = make(map[string]*resultBuffer) +var resultsBufferMu sync.Mutex func Load() error { keywords, err := model.GetKeywordsModel() @@ -95,6 +95,9 @@ func GetPage(r *http.Request) int { } func GetBuffer(searchTerm string) (*resultBuffer, error) { + resultsBufferMu.Lock() + defer resultsBufferMu.Unlock() + buffer, exists := resultsBuffer[searchTerm] if !exists { @@ -186,11 +189,13 @@ func BuildBuffer(searchTerm string) error { return 0 }) + resultsBufferMu.Lock() resultsBuffer[searchTerm] = &resultBuffer{ createdAt: time.Now(), hits: hits, results: fuzzyResults, } + resultsBufferMu.Unlock() return nil } @@ -212,9 +217,6 @@ func GetPages(buffer *resultBuffer, p int) (int, int) { } func Search(buffer *resultBuffer, page int, maxPage int) *model.ResultsModel { - buffer.mu.Lock() - defer buffer.mu.Unlock() - resultsModel := model.NewResultsModel() for i := page * pageStep; i < page*pageStep+pageStep; i++ { if i > len(buffer.results)-1 { @@ -295,11 +297,12 @@ func Search(buffer *resultBuffer, page int, maxPage int) *model.ResultsModel { } */ func ClearOldBuffers() { + resultsBufferMu.Lock() + defer resultsBufferMu.Unlock() + for key, buffer := range resultsBuffer { if time.Since(buffer.createdAt) >= time.Minute*10 { - buffer.mu.Lock() delete(resultsBuffer, key) - buffer.mu.Unlock() } } } diff --git a/cmd/view/cdsDetails.go b/cmd/view/cdsDetails.go index 74b0909..39c3944 100644 --- a/cmd/view/cdsDetails.go +++ b/cmd/view/cdsDetails.go @@ -5,8 +5,11 @@ import ( "fmt" "net/http" + db_table "api-cds-search/cmd/database/table" + "code.achtarmig.org/pas/ui/element" "code.achtarmig.org/pas/ui/element/container" + "code.achtarmig.org/pas/ui/element/form" "code.achtarmig.org/pas/ui/element/input" "code.achtarmig.org/pas/ui/element/option" "code.achtarmig.org/pas/ui/element/table" @@ -21,14 +24,22 @@ func cdsDetails() { p.OnRequest = onRequestDetails container.New(p, "Details", func(p *container.Element) { p.Justify = option.JustifyCenter - input.New(p, "q", input.TypeText, func(p *input.Element) { - p.Placeholder = "CDSViewTechnicalName" - p.Shape = option.ShapeRound - p.SetHTMX(element.HTMX{ - Method: http.MethodGet, - Target: "#DetailsTable", - URL: "/cds", - PushURL: true, + form.New(p, "qForm", func(p *form.Element) { + p.Method = http.MethodGet + p.Target = "#DetailsTable" + p.URL = "/cds" + p.PushURL = true + input.New(p, "q", input.TypeText, func(p *input.Element) { + p.ValidateInput = true + p.RequestDelay = 200 + p.Parameter = true + p.Placeholder = "CDSViewTechnicalName" + p.Shape = option.ShapeRound + + p.OnValidate = func(e *input.Element, v any) error { + _, err := db_table.GetCDSView(db_table.TechnicalName(v.(string))) + return err + } }) }) }) @@ -43,7 +54,7 @@ func cdsDetails() { } func onRequestDetails(e *view.Element, w http.ResponseWriter, r *http.Request) view.ProcessElements { - cds, _ := e.GetElement("q").(*input.Element).GetDataText() + cds, _ := e.GetElement("q").(*input.Element).GetDataString() e.Title = fmt.Sprintf("CDSDetails - %s", cds) diff --git a/cmd/view/results.go b/cmd/view/results.go index f0cddac..32f8707 100644 --- a/cmd/view/results.go +++ b/cmd/view/results.go @@ -40,7 +40,7 @@ func noResults(v *view.Element) element.ElementInterface { cont := v.GetElement("SearchResultsContainer") cont.DeleteAllChildren() - pl, _ := placeholder.New(cont, "", func(p *placeholder.Element) { + pl := placeholder.New(cont, "", func(p *placeholder.Element) { p.Icon = option.IconLoupe p.Title = "No Results" p.Subtitle = "Try refining your search term" diff --git a/cmd/view/search.go b/cmd/view/search.go index d8ee915..9b86dbb 100644 --- a/cmd/view/search.go +++ b/cmd/view/search.go @@ -19,7 +19,7 @@ func noSearch(v *view.Element) element.ElementInterface { cont := v.GetElement("SearchResultsContainer") cont.DeleteAllChildren() - pl, _ := placeholder.New(cont, "", func(p *placeholder.Element) { + pl := placeholder.New(cont, "", func(p *placeholder.Element) { p.Icon = option.IconLoupe p.Title = "Search CDS-Views" p.Subtitle = "Find CDS-Views from the Business Accelerator Hub" @@ -33,7 +33,7 @@ func searchView() { p.Title = "CDS-Search" p.OnRequest = func(v *view.Element, w http.ResponseWriter, r *http.Request) view.ProcessElements { inputQuery := v.GetElement("q").(*input.Element) - inputQueryValue, _ := v.GetElement("q").(*input.Element).GetDataText() + inputQueryValue, _ := v.GetElement("q").(*input.Element).GetDataString() searchTerm := search.GetSearchTerm(inputQueryValue) if inputQueryValue == "" { @@ -43,7 +43,7 @@ func searchView() { inputPage := v.GetElement("p").(*input.Element) inputMaxPage := v.GetElement("MaxPage").(*input.Element) - currentPage, _ := inputPage.GetDataNumber() + currentPage, _ := inputPage.GetDataInt() if inputQuery.GetDataChanged() && inputQueryValue != "" { buf, err := search.GetBuffer(searchTerm) @@ -70,25 +70,24 @@ func searchView() { return view.ProcessElements{ Partial: []element.ElementInterface{v.GetElement("SearchResultsContainer")}, - OOBUpdate: []element.ElementInterface{inputPage, inputMaxPage}, + OOBUpdate: []element.ElementInterface{v.GetElement("pagerForm")}, } } container.New(p, "MainContainer", func(p *container.Element) { p.Justify = option.JustifyCenter form.New(p, "SearchForm", func(p *form.Element) { - p.SetHTMX(element.HTMX{ - Method: http.MethodGet, - Target: "#SearchResultsContainer>*", - Swap: "outerHTML", - URL: "/search", - PushURL: true, - Params: "q, p", - }) + p.Method = http.MethodGet + p.Target = "#SearchResultsContainer>*" + p.Swap = "outerHTML" + p.URL = "/search" + p.PushURL = true + p.Params = "q, p" container.New(p, "SearchFormLinked", func(p *container.Element) { p.Justify = option.JustifyCenter p.LinkedHorizontal = true input.New(p, "q", input.TypeText, func(p *input.Element) { + p.Parameter = true p.Placeholder = "Search here" p.Shape = option.ShapeRound @@ -110,38 +109,43 @@ func searchView() { container.New(p, "CenterPager", func(p *container.Element) { p.Justify = option.JustifyCenter - container.New(p, "Pager", func(p *container.Element) { + container.New(p, "RightCenterPager", func(p *container.Element) { p.MaxWidth = 42 p.MinWidth = 10 p.Justify = option.JustifyRight - p.LinkedHorizontal = true - input.New(p, "p", input.TypeNumber, func(p *input.Element) { - p.Shape = option.ShapeRound + form.New(p, "pagerForm", func(p *form.Element) { + p.MaxWidth = -1 + p.MinWidth = 8 + p.Method = http.MethodGet + p.Include = "[name='q'], [name='p']" + p.Target = "#SearchResultsContainer>*" + p.URL = "/search" + p.Swap = "outerHTML" + p.Trigger = "change from:#p" + p.PushURL = true + container.New(p, "LinkPager", func(p *container.Element) { + p.LinkedHorizontal = true + input.New(p, "p", input.TypeNumber, func(p *input.Element) { + p.Parameter = true + p.Shape = option.ShapeRound - p.SetMin = true - p.Min = 0 + p.SetMin = true + p.Min = 0 - p.SetMax = true - p.Max = 0 + p.SetMax = true + p.Max = 0 - p.MinWidth = 4 - - p.SetHTMX(element.HTMX{ - Method: http.MethodGet, - Include: "[name='q'], [name='p']", - Target: "#SearchResultsContainer>*", - URL: "/search", - Swap: "outerHTML", - PushURL: true, + p.MinWidth = 4 + }) + input.New(p, "MaxPage", input.TypeNumber, func(p *input.Element) { + p.Disabled = true + p.Shape = option.ShapeRound + p.MinWidth = 4 + p.SetMin = true + p.SetMax = true + }) }) }) - input.New(p, "MaxPage", input.TypeNumber, func(p *input.Element) { - p.Disabled = true - p.Shape = option.ShapeRound - p.MinWidth = 4 - p.SetMin = true - p.SetMax = true - }) }) }) diff --git a/go.mod b/go.mod index 2a19c31..6908e22 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module api-cds-search go 1.23.7 require ( - code.achtarmig.org/pas/ui v0.0.0-20250520194351-de0cc4fbbcfa + code.achtarmig.org/pas/ui v0.0.0-20250523174346-c02bf81e9cf3 github.com/glebarez/go-sqlite v1.22.0 github.com/go-chi/chi v1.5.5 github.com/lithammer/fuzzysearch v1.1.8 diff --git a/go.sum b/go.sum index 703bed9..f52ec55 100644 --- a/go.sum +++ b/go.sum @@ -1,11 +1,11 @@ -code.achtarmig.org/pas/ui v0.0.0-20250517190325-787754158160 h1:nhr5BUMdLRJrj/DnO4LZdsPwgIz3vYvaf08LdND7Eu8= -code.achtarmig.org/pas/ui v0.0.0-20250517190325-787754158160/go.mod h1:4SnZejrC0bLr2Kq1yt4TBis8lo2hHA8K0m/0B2l6Uo0= -code.achtarmig.org/pas/ui v0.0.0-20250520192537-02c6f64d9a4f h1:gj8Lq5QgB8LlOxqrPPmj673gW9brX3TsYI9Ijp281Vw= -code.achtarmig.org/pas/ui v0.0.0-20250520192537-02c6f64d9a4f/go.mod h1:4SnZejrC0bLr2Kq1yt4TBis8lo2hHA8K0m/0B2l6Uo0= -code.achtarmig.org/pas/ui v0.0.0-20250520194044-0b9d43111bb8 h1:Zzr3PGFp5qVZxRrZOOWSe5Vua7s3dTjIAZmF00XglN8= -code.achtarmig.org/pas/ui v0.0.0-20250520194044-0b9d43111bb8/go.mod h1:4SnZejrC0bLr2Kq1yt4TBis8lo2hHA8K0m/0B2l6Uo0= code.achtarmig.org/pas/ui v0.0.0-20250520194351-de0cc4fbbcfa h1:rMY1ksN8wtFO8ago/jMEgz52F/pFQIZLfN1YFSyTeEg= code.achtarmig.org/pas/ui v0.0.0-20250520194351-de0cc4fbbcfa/go.mod h1:4SnZejrC0bLr2Kq1yt4TBis8lo2hHA8K0m/0B2l6Uo0= +code.achtarmig.org/pas/ui v0.0.0-20250523170703-92fa955c1776 h1:VM1tir3yk+4iuJbDRVPmsLjrk1rJ+gTRPJh6LFTMMh0= +code.achtarmig.org/pas/ui v0.0.0-20250523170703-92fa955c1776/go.mod h1:2stpDl/L6Zgd4a/r+fDMsMMuF59aKQbAIeByX1UqiQo= +code.achtarmig.org/pas/ui v0.0.0-20250523172517-f2c3e6308b7d h1:/vt1BgqVi8fW7cdZWGMOfMarIRkrQuBMBdKhwEziaGI= +code.achtarmig.org/pas/ui v0.0.0-20250523172517-f2c3e6308b7d/go.mod h1:2stpDl/L6Zgd4a/r+fDMsMMuF59aKQbAIeByX1UqiQo= +code.achtarmig.org/pas/ui v0.0.0-20250523174346-c02bf81e9cf3 h1:Lsq0qH31XZG5b9j51y9vmXx+ZBmLD5H64aPlNBVjDfc= +code.achtarmig.org/pas/ui v0.0.0-20250523174346-c02bf81e9cf3/go.mod h1:2stpDl/L6Zgd4a/r+fDMsMMuF59aKQbAIeByX1UqiQo= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ=