views: prepare home page

This commit is contained in:
Tigor Hutasuhut 2024-04-09 16:09:08 +07:00
parent e5002d8187
commit a0403539ae
8 changed files with 201 additions and 7 deletions

View file

@ -25,9 +25,6 @@ func (r *Routes) SubredditsListAPI(rw http.ResponseWriter, req *http.Request) {
}
}
func (r *Routes) SubredditsListPage(rw http.ResponseWriter, req *http.Request) {
}
func parseSubredditListQuery(req *http.Request) (params api.ListSubredditsParams) {
params.Name = req.FormValue("name")
params.Limit, _ = strconv.ParseInt(req.FormValue("limit"), 10, 64)

View file

@ -11,7 +11,20 @@ import (
func (routes *Routes) PageHome(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context()
vc := views.NewContext(routes.Config, r)
if err := homeview.Home(vc).Render(ctx, rw); err != nil {
params := parseSubredditListQuery(r)
list, err := routes.API.ListSubreddits(ctx, params)
if err != nil {
rw.WriteHeader(http.StatusInternalServerError)
}
data := homeview.Data{
SubredditsList: list,
Error: err,
}
if err := homeview.Home(vc, data).Render(ctx, rw); err != nil {
log.New(ctx).Err(err).Error("failed to render home view")
}
}

View file

@ -39,7 +39,9 @@ func (routes *Routes) registerWWWRoutes(router chi.Router) {
router.Mount("/public", http.StripPrefix("/public", http.FileServer(http.FS(routes.PublicDir))))
router.Group(func(r chi.Router) {
r.Use(chimiddleware.RequestID)
r.Use(chimiddleware.RequestLogger(middleware.ChiLogger{}))
r.Use(chimiddleware.SetHeader("Content-Type", "text/html; charset=utf-8"))
r.Get("/", routes.PageHome)
})
}

View file

@ -0,0 +1,16 @@
package components
import "github.com/tigorlazuardi/redmage/views"
templ Body(c *views.Context) {
<body class="bg-base-100 min-h-screen" hx-ext="response-targets">
@Navigation(c) {
<div class="flex">
@Navbar(c)
<main class="flex-1">
{ children... }
</main>
</div>
}
</body>
}

View file

@ -0,0 +1,7 @@
package components
templ Container() {
<div class="p-8">
{ children... }
</div>
}

View file

@ -0,0 +1,106 @@
package components
import "github.com/tigorlazuardi/redmage/views"
import "strings"
templ Navigation(c *views.Context) {
<div class="drawer">
<input id="drawer-toggle" type="checkbox" class="drawer-toggle"/>
<div class="drawer-content">
<header class="navbar bg-base-200 lg:hidden">
<div class="flex-none">
<label for="drawer-toggle" class="btn btn-square btn-ghost drawer-button" onclick="console.log(this)">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="inline-block w-5 h-5 stroke-current"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path></svg>
</label>
</div>
<div class="flex-none">
<button class="btn btn-square btn-ghost">
@navLogo()
</button>
</div>
<div class="flex-1">
<a href="/" class="btn btn-ghost text-xl">Redmage</a>
</div>
</header>
{ children... }
</div>
<nav class="drawer-side" hx-boost="true">
<label for="drawer-toggle" class="drawer-overlay"></label>
<div class="menu py-4 min-w-[15rem] min-h-full bg-base-200 text-base-content">
<div class="flex flex-col items-center min-w-[180px] max-w-[280px]">
@navLogo()
<span class="font-bold">Redmage</span>
</div>
<div class="divider"></div>
<ul>
<a href="/" class={ classForNavItem(c, "/") }>
<li class="hover:bg-accent hover:text-neutral-50 py-2 pl-4 hover:font-bold">Home</li>
</a>
<a href="/about" class={ classForNavItem(c, "/about") }>
<li class="hover:bg-accent hover:text-neutral-50 py-2 pl-4 hover:font-bold">About</li>
</a>
<a href="/config" class={ classForNavItem(c, "/config") }>
<li class="hover:bg-accent hover:text-neutral-50 py-2 pl-4 hover:font-bold">Config</li>
</a>
<a href="/browse" class={ classForNavItem(c, "/browse") }>
<li class="hover:bg-accent hover:text-neutral-50 py-2 pl-4 hover:font-bold">Browse</li>
</a>
</ul>
</div>
</nav>
</div>
}
func cx(m map[string]bool) string {
var res string
for k, v := range m {
if v {
res += k + " "
}
}
return res
}
func classForNavItem(c *views.Context, prefix string) string {
classNames := map[string]bool{}
if prefix == "/" && c.Request.URL.Path == "/" {
classNames["font-bold"] = true
return cx(classNames)
} else if strings.HasPrefix(c.Request.URL.Path, prefix) && prefix != "/" {
classNames["font-bold"] = true
}
return cx(classNames)
}
templ navLogo() {
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 18.75 7.5-7.5 7.5 7.5"></path>
<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 7.5-7.5 7.5 7.5"></path>
</svg>
}
templ Navbar(c *views.Context) {
<header class="hidden lg:inline-block bg-base-200 min-w-[200px] w-[15vw] max-w-[300px] min-h-screen">
<div class="flex flex-col items-center min-w-[180px] max-w-[280px] mx-auto">
@navLogo()
<span class="font-bold">Redmage</span>
</div>
<div class="divider"></div>
<nav class="pt-4" hx-boost="true">
<ul class="flex flex-col flex-wrap">
<a href="/" class={ classForNavItem(c, "/") }>
<li class="hover:bg-accent hover:text-neutral-50 py-2 text-center hover:font-bold">Home</li>
</a>
<a href="/about" class={ classForNavItem(c, "/about") }>
<li class="hover:bg-accent hover:text-neutral-50 py-2 text-center hover:font-bold">About</li>
</a>
<a href="/config" class={ classForNavItem(c, "/config") }>
<li class="hover:bg-accent hover:text-neutral-50 py-2 text-center hover:font-bold">Config</li>
</a>
<a href="/browse" class={ classForNavItem(c, "/browse") }>
<li class="hover:bg-accent hover:text-neutral-50 py-2 text-center hover:font-bold">Browse</li>
</a>
</ul>
</nav>
</header>
}

View file

@ -0,0 +1,33 @@
package components
import "fmt"
import "strings"
templ SuccessToast(message string, args ...any) {
<div
onclick="this.remove();"
role="alert"
class="alert alert-success hover:bg-accent cursor-pointer transition-all"
>
<svg xmlns="http://www.w3.org/2000/svg" class="stroke-current shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
<div class="text-base-100 flex flex-col gap-1">
for _, line := range strings.Split(fmt.Sprintf(message, args...), "\n") {
<p class="my-0">{ line }</p>
}
</div>
</div>
}
templ ErrorToast(message string, args ...any) {
<div
onclick="this.remove();"
class="alert alert-error hover:bg-accent cursor-pointer"
>
<svg xmlns="http://www.w3.org/2000/svg" class="stroke-current shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
<div class="text-base-100 flex flex-col gap-1">
for _, line := range strings.Split(fmt.Sprintf(message, args...), "\n") {
<p class="my-0">{ line }</p>
}
</div>
</div>
}

View file

@ -2,10 +2,30 @@ package homeview
import "github.com/tigorlazuardi/redmage/views/components"
import "github.com/tigorlazuardi/redmage/views"
import "github.com/tigorlazuardi/redmage/api"
templ Home(vc *views.Context) {
type Data struct {
SubredditsList api.ListSubredditsResult
Error error
}
templ Home(c *views.Context, data Data) {
@components.Doctype() {
@components.Head(vc)
<div class="text-secondary">Hello World</div>
@components.Head(c)
@components.Body(c) {
@components.Container() {
if data.Error != nil {
@components.ErrorToast(data.Error.Error())
} else {
@home(c, data)
}
}
}
}
}
templ home(c *views.Context, data Data) {
<div class="prose">
<h1>Recently Added</h1>
</div>
}