Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions internal/server/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/gofs-cli/template/internal/ui/pages/notfound"
"github.com/gofs-cli/template/internal/ui/pages/page1"
"github.com/gofs-cli/template/internal/ui/pages/page2"
progressbar "github.com/gofs-cli/template/internal/ui/pages/progress-bar"
"github.com/gofs-cli/template/internal/ui/pages/validation"
)

Expand Down Expand Up @@ -56,6 +57,12 @@ func (s *Server) Routes() {
routesMux.Handle("GET /active-search", activesearch.Index())
routesMux.Handle("POST /active-search/search", activesearch.Search())

// progress bar example
routesMux.Handle("GET /progress-bar", progressbar.Index())
routesMux.Handle("POST /progress-bar/start", progressbar.StartProgressBar())
routesMux.Handle("GET /progress-bar/job/progress", progressbar.RunProgressBar())
routesMux.Handle("GET /progress-bar/job", progressbar.CompleteProgressBar())

routesMux.Handle("GET /modal", home.Modal())

routesMux.Handle("GET /page1", page1.Index())
Expand Down
60 changes: 60 additions & 0 deletions internal/ui/pages/progress-bar/handlers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package progressbar

import (
"math/rand"
"net/http"
"strconv"
"sync"

"github.com/a-h/templ"
"github.com/gofs-cli/template/internal/ui"
"github.com/gofs-cli/template/internal/ui/components/header"
)

type Job struct {
mu sync.Mutex
progress int
}

// Global variable to store the current job state
var currentJob = &Job{}

func Index() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
templ.Handler(ui.IndexPage(layout(header.Header(), body()))).ServeHTTP(w, r)
})
}

func StartProgressBar() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
currentJob.mu.Lock()
currentJob.progress = 0
currentJob.mu.Unlock()

templ.Handler(startProgressBar()).ServeHTTP(w, r)
})
}

func RunProgressBar() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
currentJob.mu.Lock()
defer currentJob.mu.Unlock()

if currentJob.progress < 100 {
currentJob.progress += rand.Intn(16)
}

if currentJob.progress >= 100 {
w.Header().Set("HX-Trigger", "done")
templ.Handler(inProgress(strconv.Itoa(currentJob.progress))).ServeHTTP(w, r)
} else {
templ.Handler(inProgress(strconv.Itoa(currentJob.progress))).ServeHTTP(w, r)
}
})
}

func CompleteProgressBar() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
templ.Handler(completeProgressBar()).ServeHTTP(w, r)
})
}
98 changes: 98 additions & 0 deletions internal/ui/pages/progress-bar/index.templ
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package progressbar

import "fmt"

css classLayout() {
display: grid;
}

css progress() {
height: 20px;
margin-bottom: 20px;
overflow: hidden;
background-color: #f5f5f5;
border-radius: 4px;
box-shadow: inset 0 1px 2px rgba(0,0,0,.1);
}

css progressbar() {
float: left;
width: 0%;
height: 100%;
font-size: 12px;
line-height: 20px;
color: #fff;
text-align: center;
background-color: #337ab7;
-webkit-box-shadow: inset 0 -1px 0 rgba(0,0,0,.15);
box-shadow: inset 0 -1px 0 rgba(0,0,0,.15);
-webkit-transition: width .6s ease;
-o-transition: width .6s ease;
transition: width .6s ease;
}

css loading(percent string) {
width: { fmt.Sprintf("%s%%", percent) };
}

templ layout(header, body templ.Component) {
<main class={ classLayout() }>
<div>
@header
</div>
<div>
@body
</div>
</main>
}

templ body() {
<h2>Progress Bar</h2>
<div hx-target="this" hx-swap="outerHTML">
<h3>Start Progress</h3>
<button class="btn primary" hx-post="/progress-bar/start">
Start Job
</button>
</div>
}

templ startProgressBar() {
<div hx-trigger="done" hx-get="/progress-bar/job" hx-swap="outerHTML" hx-target="this">
<h3 role="status" id="pblabel" tabindex="-1" autofocus>Running</h3>
<div
hx-get="/progress-bar/job/progress"
hx-trigger="every 600ms"
hx-target="this"
hx-swap="innerHTMl"
>
<div class={ progress() } role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0" aria-labelledby="pblabel">
<div id="pb" class={ progressbar(), loading("0") }></div>
</div>
</div>
</div>
}

templ inProgress(val string) {
<div class={ progress() } role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow={ val } aria-labelledby="pblabel">
<div id="pb" class={ progressbar(), loading(val) }></div>
</div>
}

templ completeProgressBar() {
<div hx-trigger="done" hx-get="/progress-bar/job" hx-swap="outerHTML" hx-target="this">
<h3 role="status" id="pblabel" tabindex="-1" autofocus>Complete</h3>
<div
hx-get="/progress-bar/job/progress"
hx-trigger="none"
hx-target="this"
hx-swap="innerHTML"
>
<div class={ progress() } role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="122" aria-labelledby="pblabel">
<div id="pb" class={ progressbar() } style="width:122%"></div>
</div>
</div>
<button id="restart-btn" class="btn primary" hx-post="/progress-bar/start" classes="add show:600ms">
Restart Job
</button>
</div>
}
Loading
Loading