cerrado @ 349a3d1ff36a436261b1b65b870f8f262f06584f

feat: Add bare bones project list
diff --git a/main.go b/main.go
index d2452e8a5766b22174b55f2ffbdcd2bc2149a954..0ba2dbc9553dcab2cbb3b5c0b3172c79c7dda650 100644
--- a/main.go
+++ b/main.go
@@ -1,25 +1,17 @@
 package main
 
 import (
-	"bytes"
 	"context"
-	"encoding/json"
 	"flag"
-	"io/fs"
 	"log/slog"
 	"net/http"
 	"os"
 	"os/signal"
 	"time"
 
-	"github.com/alecthomas/chroma/v2/formatters/html"
-	"github.com/alecthomas/chroma/v2/lexers"
-	"github.com/alecthomas/chroma/v2/styles"
-
-	"git.gabrielgio.me/cerrado/pkg/config"
+	"git.gabrielgio.me/cerrado/pkg/handler"
+	"git.gabrielgio.me/cerrado/pkg/service"
 	"git.gabrielgio.me/cerrado/pkg/worker"
-	"git.gabrielgio.me/cerrado/static"
-	"git.gabrielgio.me/cerrado/templates"
 )
 
 func main() {
@@ -41,57 +33,20 @@ 	flag.Parse()
 
 	mux := http.NewServeMux()
 
-	staticFs, err := fs.Sub(static.Static, ".")
+	staticHandler, err := handler.NewStaticHander("/static/")
 	if err != nil {
 		return err
 	}
 
-	mux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.FS(staticFs))))
-	mux.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) {
-		slog.Info("Handling index")
+	// services
+	gitService := service.NewGitService()
 
-		f, err := os.Open(*configPath)
-		if err != nil {
-			slog.Error("Error openning config file json", "error", err, "path", *configPath)
-			return
-		}
+	//handlers
+	gitHandler := handler.NewGitHandler(gitService)
 
-		c, err := config.Parse(f)
-		if err != nil {
-			slog.Error("Error parsing config", "error", err, "path", *configPath)
-			return
-		}
-
-		b, err := json.MarshalIndent(c, "", "	")
-		if err != nil {
-			slog.Error("Error parsing json", "error", err)
-			return
-		}
-
-		lexer := lexers.Get("json")
-		style := styles.Get("monokailight")
-		formatter := html.New(
-			html.WithLineNumbers(true),
-		)
-		iterator, err := lexer.Tokenise(nil, string(b))
-		if err != nil {
-			slog.Error("Error tokenise", "error", err)
-			return
-		}
-
-		var code bytes.Buffer
-		err = formatter.Format(&code, style, iterator)
-		if err != nil {
-			slog.Error("Error format", "error", err)
-			return
-		}
-
-		hello := &templates.HelloPage{
-			Body: code.String(),
-		}
-
-		templates.WritePageTemplate(w, hello)
-	})
+	mux.Handle("/static/", staticHandler)
+	mux.HandleFunc("/config", handler.ConfigFile(*configPath))
+	mux.HandleFunc("/", gitHandler.List)
 	serverTask := worker.NewServerTask(&http.Server{Handler: mux, Addr: "0.0.0.0:8080"})
 
 	pool := worker.NewTaskPool()
diff --git a/pkg/handler/git.go b/pkg/handler/git.go
new file mode 100644
index 0000000000000000000000000000000000000000..5b0912187f278158f0ab3ff7287a1a2f06e0e6df
--- /dev/null
+++ b/pkg/handler/git.go
@@ -0,0 +1,21 @@
+package handler
+
+import (
+	"net/http"
+
+	"git.gabrielgio.me/cerrado/pkg/service"
+	"git.gabrielgio.me/cerrado/templates"
+)
+
+type GitHandler struct {
+	gitService *service.GitService
+}
+
+func NewGitHandler(gitService *service.GitService) *GitHandler {
+	return &GitHandler{gitService}
+}
+
+func (g *GitHandler) List(w http.ResponseWriter, _ *http.Request) {
+	gitList := &templates.GitListPage{g.gitService.ListRepositories()}
+	templates.WritePageTemplate(w, gitList)
+}
diff --git a/pkg/handler/static.go b/pkg/handler/static.go
new file mode 100644
index 0000000000000000000000000000000000000000..9f312f41f09ef14dad4649bfaa0b76d61f8abb5e
--- /dev/null
+++ b/pkg/handler/static.go
@@ -0,0 +1,18 @@
+package handler
+
+import (
+	"io/fs"
+	"net/http"
+
+	"git.gabrielgio.me/cerrado/static"
+)
+
+func NewStaticHander(prefix string) (http.Handler, error) {
+	staticFs, err := fs.Sub(static.Static, ".")
+	if err != nil {
+		return nil, err
+	}
+
+	handler := http.StripPrefix(prefix, http.FileServer(http.FS(staticFs)))
+	return handler, nil
+}
diff --git a/pkg/handler/status.go b/pkg/handler/status.go
new file mode 100644
index 0000000000000000000000000000000000000000..2a84a7e0e00ce79a7236eda6e444c6c3220209b9
--- /dev/null
+++ b/pkg/handler/status.go
@@ -0,0 +1,62 @@
+package handler
+
+import (
+	"bytes"
+	"encoding/json"
+	"log/slog"
+	"net/http"
+	"os"
+
+	"github.com/alecthomas/chroma/v2/formatters/html"
+	"github.com/alecthomas/chroma/v2/lexers"
+	"github.com/alecthomas/chroma/v2/styles"
+
+	"git.gabrielgio.me/cerrado/pkg/config"
+	"git.gabrielgio.me/cerrado/templates"
+)
+
+func ConfigFile(configPath string) func(http.ResponseWriter, *http.Request) {
+	return func(w http.ResponseWriter, _ *http.Request) {
+		f, err := os.Open(configPath)
+		if err != nil {
+			slog.Error("Error openning config file json", "error", err, "path", configPath)
+			return
+		}
+
+		c, err := config.Parse(f)
+		if err != nil {
+			slog.Error("Error parsing config", "error", err, "path", configPath)
+			return
+		}
+
+		b, err := json.MarshalIndent(c, "", "	")
+		if err != nil {
+			slog.Error("Error parsing json", "error", err)
+			return
+		}
+
+		lexer := lexers.Get("json")
+		style := styles.Get("monokailight")
+		formatter := html.New(
+			html.WithLineNumbers(true),
+		)
+		iterator, err := lexer.Tokenise(nil, string(b))
+		if err != nil {
+			slog.Error("Error tokenise", "error", err)
+			return
+		}
+
+		var code bytes.Buffer
+		err = formatter.Format(&code, style, iterator)
+		if err != nil {
+			slog.Error("Error format", "error", err)
+			return
+		}
+
+		hello := &templates.HelloPage{
+			Body: code.String(),
+		}
+
+		templates.WritePageTemplate(w, hello)
+	}
+}
diff --git a/pkg/service/git.go b/pkg/service/git.go
new file mode 100644
index 0000000000000000000000000000000000000000..0415ceeee803e69b7e73d69b5875edd6321cfffd
--- /dev/null
+++ b/pkg/service/git.go
@@ -0,0 +1,30 @@
+package service
+
+import "fmt"
+
+type (
+	GitService struct{}
+	Repository struct {
+		Name        string
+		Title       string
+		Description string
+	}
+)
+
+func NewGitService() *GitService {
+	return &GitService{}
+}
+
+func (g *GitService) ListRepositories() []*Repository {
+	repos := make([]*Repository, 10)
+
+	for i := range 10 {
+		repos[i] = &Repository{
+			Name:        fmt.Sprintf("repository-%d", i),
+			Title:       fmt.Sprintf("Repository %d", i),
+			Description: fmt.Sprintf("This is a description for repository %d", i),
+		}
+	}
+
+	return repos
+}
diff --git a/scss/main.scss b/scss/main.scss
index 7273c9f36be42a2a85fa8774639400a13bb7668a..eb8a6fe7dc52e09b62850258544a966c374925d2 100644
--- a/scss/main.scss
+++ b/scss/main.scss
@@ -1,4 +1,9 @@
+//$card-border-width: 0;
+$card-border-radius: 0;
+$card-cap-padding-y: 0;
+$card-cap-padding-x: 0;
 
+// basic functionality
 @import "bootstrap/scss/_functions.scss";
 @import "bootstrap/scss/_variables.scss";
 @import "bootstrap/scss/_variables-dark.scss";
@@ -6,14 +11,16 @@ @import "bootstrap/scss/_maps.scss";
 @import "bootstrap/scss/_mixins.scss";
 @import "bootstrap/scss/_utilities.scss";
 
-
+// added component
 @import "bootstrap/scss/_root.scss";
 @import "bootstrap/scss/_containers.scss";
 @import "bootstrap/scss/_nav.scss";
 @import "bootstrap/scss/_navbar.scss";
+@import "bootstrap/scss/_card.scss";
+@import "bootstrap/scss/_grid.scss";
 
 
 body {
     font-family: $font-family-monospace;
-
+    margin: 0;
 }
diff --git a/templates/gitlist.qtpl b/templates/gitlist.qtpl
new file mode 100644
index 0000000000000000000000000000000000000000..10a89da1a91734e006f2ef7e191ae51bc269ec01
--- /dev/null
+++ b/templates/gitlist.qtpl
@@ -0,0 +1,34 @@
+{% import "git.gabrielgio.me/cerrado/pkg/service" %}
+{% import "git.gabrielgio.me/cerrado/pkg/u" %}
+
+
+{% code
+type GitListPage struct {
+    Respositories []*service.Repository
+}
+%}
+
+{% func (p *GitListPage) Title() %}Git | List{% endfunc %}
+
+{% func (p *GitListPage) Content() %}
+{% for _, c := range u.ChunkBy(p.Respositories, 3) %}
+<div class="row">
+  {% for _, r := range c %}
+  <div class="col-md-4 g-0">
+    <div class="card">
+      <div class="card-header">
+        {%s r.Title %}
+      </div>
+      <div class="card-body">
+        <p class="card-text">{%s r.Description %}</p>
+        <a href="/{%s r.Name %}" class="btn btn-primary">go to repository</a>
+      </div>
+    </div>
+  </div>
+  {% endfor %}
+</div>
+{% endfor %}
+{% endfunc %}
+
+{% func (p *GitListPage) Script() %}
+{% endfunc %}
diff --git a/templates/gitlist.qtpl.go b/templates/gitlist.qtpl.go
new file mode 100644
index 0000000000000000000000000000000000000000..b02eeadf929a41ad4b3621deb6f5b7cb34be5454
--- /dev/null
+++ b/templates/gitlist.qtpl.go
@@ -0,0 +1,175 @@
+// Code generated by qtc from "gitlist.qtpl". DO NOT EDIT.
+// See https://github.com/valyala/quicktemplate for details.
+
+//line gitlist.qtpl:1
+package templates
+
+//line gitlist.qtpl:1
+import "git.gabrielgio.me/cerrado/pkg/service"
+
+//line gitlist.qtpl:2
+import "git.gabrielgio.me/cerrado/pkg/u"
+
+//line gitlist.qtpl:5
+import (
+	qtio422016 "io"
+
+	qt422016 "github.com/valyala/quicktemplate"
+)
+
+//line gitlist.qtpl:5
+var (
+	_ = qtio422016.Copy
+	_ = qt422016.AcquireByteBuffer
+)
+
+//line gitlist.qtpl:6
+type GitListPage struct {
+	Respositories []*service.Repository
+}
+
+//line gitlist.qtpl:11
+func (p *GitListPage) StreamTitle(qw422016 *qt422016.Writer) {
+//line gitlist.qtpl:11
+	qw422016.N().S(`Git | List`)
+//line gitlist.qtpl:11
+}
+
+//line gitlist.qtpl:11
+func (p *GitListPage) WriteTitle(qq422016 qtio422016.Writer) {
+//line gitlist.qtpl:11
+	qw422016 := qt422016.AcquireWriter(qq422016)
+//line gitlist.qtpl:11
+	p.StreamTitle(qw422016)
+//line gitlist.qtpl:11
+	qt422016.ReleaseWriter(qw422016)
+//line gitlist.qtpl:11
+}
+
+//line gitlist.qtpl:11
+func (p *GitListPage) Title() string {
+//line gitlist.qtpl:11
+	qb422016 := qt422016.AcquireByteBuffer()
+//line gitlist.qtpl:11
+	p.WriteTitle(qb422016)
+//line gitlist.qtpl:11
+	qs422016 := string(qb422016.B)
+//line gitlist.qtpl:11
+	qt422016.ReleaseByteBuffer(qb422016)
+//line gitlist.qtpl:11
+	return qs422016
+//line gitlist.qtpl:11
+}
+
+//line gitlist.qtpl:13
+func (p *GitListPage) StreamContent(qw422016 *qt422016.Writer) {
+//line gitlist.qtpl:13
+	qw422016.N().S(`
+`)
+//line gitlist.qtpl:14
+	for _, c := range u.ChunkBy(p.Respositories, 3) {
+//line gitlist.qtpl:14
+		qw422016.N().S(`
+<div class="row">
+  `)
+//line gitlist.qtpl:16
+		for _, r := range c {
+//line gitlist.qtpl:16
+			qw422016.N().S(`
+  <div class="col-md-4 g-0">
+    <div class="card">
+      <div class="card-header">
+        `)
+//line gitlist.qtpl:20
+			qw422016.E().S(r.Title)
+//line gitlist.qtpl:20
+			qw422016.N().S(`
+      </div>
+      <div class="card-body">
+        <p class="card-text">`)
+//line gitlist.qtpl:23
+			qw422016.E().S(r.Description)
+//line gitlist.qtpl:23
+			qw422016.N().S(`</p>
+        <a href="/`)
+//line gitlist.qtpl:24
+			qw422016.E().S(r.Name)
+//line gitlist.qtpl:24
+			qw422016.N().S(`" class="btn btn-primary">go to repository</a>
+      </div>
+    </div>
+  </div>
+  `)
+//line gitlist.qtpl:28
+		}
+//line gitlist.qtpl:28
+		qw422016.N().S(`
+</div>
+`)
+//line gitlist.qtpl:30
+	}
+//line gitlist.qtpl:30
+	qw422016.N().S(`
+`)
+//line gitlist.qtpl:31
+}
+
+//line gitlist.qtpl:31
+func (p *GitListPage) WriteContent(qq422016 qtio422016.Writer) {
+//line gitlist.qtpl:31
+	qw422016 := qt422016.AcquireWriter(qq422016)
+//line gitlist.qtpl:31
+	p.StreamContent(qw422016)
+//line gitlist.qtpl:31
+	qt422016.ReleaseWriter(qw422016)
+//line gitlist.qtpl:31
+}
+
+//line gitlist.qtpl:31
+func (p *GitListPage) Content() string {
+//line gitlist.qtpl:31
+	qb422016 := qt422016.AcquireByteBuffer()
+//line gitlist.qtpl:31
+	p.WriteContent(qb422016)
+//line gitlist.qtpl:31
+	qs422016 := string(qb422016.B)
+//line gitlist.qtpl:31
+	qt422016.ReleaseByteBuffer(qb422016)
+//line gitlist.qtpl:31
+	return qs422016
+//line gitlist.qtpl:31
+}
+
+//line gitlist.qtpl:33
+func (p *GitListPage) StreamScript(qw422016 *qt422016.Writer) {
+//line gitlist.qtpl:33
+	qw422016.N().S(`
+`)
+//line gitlist.qtpl:34
+}
+
+//line gitlist.qtpl:34
+func (p *GitListPage) WriteScript(qq422016 qtio422016.Writer) {
+//line gitlist.qtpl:34
+	qw422016 := qt422016.AcquireWriter(qq422016)
+//line gitlist.qtpl:34
+	p.StreamScript(qw422016)
+//line gitlist.qtpl:34
+	qt422016.ReleaseWriter(qw422016)
+//line gitlist.qtpl:34
+}
+
+//line gitlist.qtpl:34
+func (p *GitListPage) Script() string {
+//line gitlist.qtpl:34
+	qb422016 := qt422016.AcquireByteBuffer()
+//line gitlist.qtpl:34
+	p.WriteScript(qb422016)
+//line gitlist.qtpl:34
+	qs422016 := string(qb422016.B)
+//line gitlist.qtpl:34
+	qt422016.ReleaseByteBuffer(qb422016)
+//line gitlist.qtpl:34
+	return qs422016
+//line gitlist.qtpl:34
+}
diff --git a/templates/helloworld.qtpl b/templates/helloworld.qtpl
index 66287eac78fadb51d34da5420d2700ff4b4bb511..45c5595820b5fc751efd9f83275cb86da2d11ddc 100644
--- a/templates/helloworld.qtpl
+++ b/templates/helloworld.qtpl
@@ -7,8 +7,6 @@
 {% func (p *HelloPage) Title() %}Hello{% endfunc %}
 
 {% func (p *HelloPage) Content() %}
-HelloWorld
-
 {%s= p.Body %}
 {% endfunc %}
 
diff --git a/templates/helloworld.qtpl.go b/templates/helloworld.qtpl.go
index a12455fe6c09e8027a97fd8b4a1fe30179553928..61c8f75a74d772c57fd5124d9966cfdbcba1f45c 100644
--- a/templates/helloworld.qtpl.go
+++ b/templates/helloworld.qtpl.go
@@ -59,73 +59,71 @@ //line helloworld.qtpl:9
 func (p *HelloPage) StreamContent(qw422016 *qt422016.Writer) {
 //line helloworld.qtpl:9
 	qw422016.N().S(`
-HelloWorld
-
 `)
-//line helloworld.qtpl:12
+//line helloworld.qtpl:10
 	qw422016.N().S(p.Body)
-//line helloworld.qtpl:12
+//line helloworld.qtpl:10
 	qw422016.N().S(`
 `)
-//line helloworld.qtpl:13
+//line helloworld.qtpl:11
 }
 
-//line helloworld.qtpl:13
+//line helloworld.qtpl:11
 func (p *HelloPage) WriteContent(qq422016 qtio422016.Writer) {
-//line helloworld.qtpl:13
+//line helloworld.qtpl:11
 	qw422016 := qt422016.AcquireWriter(qq422016)
-//line helloworld.qtpl:13
+//line helloworld.qtpl:11
 	p.StreamContent(qw422016)
-//line helloworld.qtpl:13
+//line helloworld.qtpl:11
 	qt422016.ReleaseWriter(qw422016)
-//line helloworld.qtpl:13
+//line helloworld.qtpl:11
 }
 
-//line helloworld.qtpl:13
+//line helloworld.qtpl:11
 func (p *HelloPage) Content() string {
-//line helloworld.qtpl:13
+//line helloworld.qtpl:11
 	qb422016 := qt422016.AcquireByteBuffer()
-//line helloworld.qtpl:13
+//line helloworld.qtpl:11
 	p.WriteContent(qb422016)
-//line helloworld.qtpl:13
+//line helloworld.qtpl:11
 	qs422016 := string(qb422016.B)
-//line helloworld.qtpl:13
+//line helloworld.qtpl:11
 	qt422016.ReleaseByteBuffer(qb422016)
-//line helloworld.qtpl:13
+//line helloworld.qtpl:11
 	return qs422016
-//line helloworld.qtpl:13
+//line helloworld.qtpl:11
 }
 
-//line helloworld.qtpl:15
+//line helloworld.qtpl:13
 func (p *HelloPage) StreamScript(qw422016 *qt422016.Writer) {
-//line helloworld.qtpl:15
+//line helloworld.qtpl:13
 	qw422016.N().S(`
 `)
-//line helloworld.qtpl:16
+//line helloworld.qtpl:14
 }
 
-//line helloworld.qtpl:16
+//line helloworld.qtpl:14
 func (p *HelloPage) WriteScript(qq422016 qtio422016.Writer) {
-//line helloworld.qtpl:16
+//line helloworld.qtpl:14
 	qw422016 := qt422016.AcquireWriter(qq422016)
-//line helloworld.qtpl:16
+//line helloworld.qtpl:14
 	p.StreamScript(qw422016)
-//line helloworld.qtpl:16
+//line helloworld.qtpl:14
 	qt422016.ReleaseWriter(qw422016)
-//line helloworld.qtpl:16
+//line helloworld.qtpl:14
 }
 
-//line helloworld.qtpl:16
+//line helloworld.qtpl:14
 func (p *HelloPage) Script() string {
-//line helloworld.qtpl:16
+//line helloworld.qtpl:14
 	qb422016 := qt422016.AcquireByteBuffer()
-//line helloworld.qtpl:16
+//line helloworld.qtpl:14
 	p.WriteScript(qb422016)
-//line helloworld.qtpl:16
+//line helloworld.qtpl:14
 	qs422016 := string(qb422016.B)
-//line helloworld.qtpl:16
+//line helloworld.qtpl:14
 	qt422016.ReleaseByteBuffer(qb422016)
-//line helloworld.qtpl:16
+//line helloworld.qtpl:14
 	return qs422016
-//line helloworld.qtpl:16
+//line helloworld.qtpl:14
 }