views: prepare home page
This commit is contained in:
parent
e5002d8187
commit
a0403539ae
|
@ -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) {
|
func parseSubredditListQuery(req *http.Request) (params api.ListSubredditsParams) {
|
||||||
params.Name = req.FormValue("name")
|
params.Name = req.FormValue("name")
|
||||||
params.Limit, _ = strconv.ParseInt(req.FormValue("limit"), 10, 64)
|
params.Limit, _ = strconv.ParseInt(req.FormValue("limit"), 10, 64)
|
|
@ -11,7 +11,20 @@ import (
|
||||||
func (routes *Routes) PageHome(rw http.ResponseWriter, r *http.Request) {
|
func (routes *Routes) PageHome(rw http.ResponseWriter, r *http.Request) {
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
vc := views.NewContext(routes.Config, r)
|
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")
|
log.New(ctx).Err(err).Error("failed to render home view")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,9 @@ func (routes *Routes) registerWWWRoutes(router chi.Router) {
|
||||||
router.Mount("/public", http.StripPrefix("/public", http.FileServer(http.FS(routes.PublicDir))))
|
router.Mount("/public", http.StripPrefix("/public", http.FileServer(http.FS(routes.PublicDir))))
|
||||||
|
|
||||||
router.Group(func(r chi.Router) {
|
router.Group(func(r chi.Router) {
|
||||||
|
r.Use(chimiddleware.RequestID)
|
||||||
r.Use(chimiddleware.RequestLogger(middleware.ChiLogger{}))
|
r.Use(chimiddleware.RequestLogger(middleware.ChiLogger{}))
|
||||||
|
r.Use(chimiddleware.SetHeader("Content-Type", "text/html; charset=utf-8"))
|
||||||
r.Get("/", routes.PageHome)
|
r.Get("/", routes.PageHome)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
16
views/components/body.templ
Normal file
16
views/components/body.templ
Normal 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>
|
||||||
|
}
|
7
views/components/container.templ
Normal file
7
views/components/container.templ
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
package components
|
||||||
|
|
||||||
|
templ Container() {
|
||||||
|
<div class="p-8">
|
||||||
|
{ children... }
|
||||||
|
</div>
|
||||||
|
}
|
106
views/components/navigation.templ
Normal file
106
views/components/navigation.templ
Normal 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>
|
||||||
|
}
|
33
views/components/toasts.templ
Normal file
33
views/components/toasts.templ
Normal 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>
|
||||||
|
}
|
|
@ -2,10 +2,30 @@ package homeview
|
||||||
|
|
||||||
import "github.com/tigorlazuardi/redmage/views/components"
|
import "github.com/tigorlazuardi/redmage/views/components"
|
||||||
import "github.com/tigorlazuardi/redmage/views"
|
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.Doctype() {
|
||||||
@components.Head(vc)
|
@components.Head(c)
|
||||||
<div class="text-secondary">Hello World</div>
|
@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>
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue