view(subreddits-list): added filter bar

This commit is contained in:
Tigor Hutasuhut 2024-05-28 16:39:38 +07:00
parent d2b81567e3
commit 4f3ba41237
4 changed files with 96 additions and 21 deletions

View file

@ -20,6 +20,7 @@ type ListSubredditsParams struct {
Offset int64 Offset int64
OrderBy string OrderBy string
Sort string Sort string
NSFW int
} }
func (l *ListSubredditsParams) FillFromQuery(q Queryable) { func (l *ListSubredditsParams) FillFromQuery(q Queryable) {
@ -37,6 +38,11 @@ func (l *ListSubredditsParams) FillFromQuery(q Queryable) {
} }
l.OrderBy = q.Get("order_by") l.OrderBy = q.Get("order_by")
l.Sort = strings.ToLower(q.Get("sort")) l.Sort = strings.ToLower(q.Get("sort"))
if nsfw, err := strconv.Atoi(q.Get("nsfw")); err == nil {
l.NSFW = nsfw
} else {
l.NSFW = -1
}
} }
func (l ListSubredditsParams) Query() (expr []bob.Mod[*dialect.SelectQuery]) { func (l ListSubredditsParams) Query() (expr []bob.Mod[*dialect.SelectQuery]) {
@ -56,12 +62,23 @@ func (l ListSubredditsParams) Query() (expr []bob.Mod[*dialect.SelectQuery]) {
expr = append(expr, sm.OrderBy(sqlite.Quote(l.OrderBy)).Asc()) expr = append(expr, sm.OrderBy(sqlite.Quote(l.OrderBy)).Asc())
} }
} else { } else {
expr = append(expr, sm.OrderBy(models.SubredditColumns.Name).Asc()) expr = append(expr, sm.OrderBy(models.SubredditColumns.UpdatedAt).Desc())
} }
return expr return expr
} }
func (l ListSubredditsParams) ImageCoverQuery(subname string) (expr []bob.Mod[*dialect.SelectQuery]) {
expr = make([]bob.Mod[*dialect.SelectQuery], 0, 4)
expr = append(expr, models.SelectWhere.Images.Subreddit.EQ(subname))
if l.NSFW >= 0 {
expr = append(expr, models.SelectWhere.Images.NSFW.EQ(int32(l.NSFW)))
}
expr = append(expr, sm.Limit(1))
expr = append(expr, sm.OrderBy(models.ImageColumns.CreatedAt).Desc())
return expr
}
func (l ListSubredditsParams) CountQuery() (expr []bob.Mod[*dialect.SelectQuery]) { func (l ListSubredditsParams) CountQuery() (expr []bob.Mod[*dialect.SelectQuery]) {
if l.Q != "" { if l.Q != "" {
expr = append(expr, models.SelectWhere.Subreddits.Name.Like("%"+l.Q+"%")) expr = append(expr, models.SelectWhere.Subreddits.Name.Like("%"+l.Q+"%"))
@ -112,11 +129,7 @@ func (api *API) ListSubredditsWithCover(ctx context.Context, arg ListSubredditsP
// //
// Subreddit list is expected to be small, so this should be fine since SQLITE has no network latency. // Subreddit list is expected to be small, so this should be fine since SQLITE has no network latency.
for _, subreddit := range result.Data { for _, subreddit := range result.Data {
subreddit.R.Images, err = models.Images.Query(ctx, api.db, subreddit.R.Images, err = models.Images.Query(ctx, api.db, arg.ImageCoverQuery(subreddit.Name)...).All()
models.SelectWhere.Images.Subreddit.EQ(subreddit.Name),
sm.Limit(1),
sm.OrderBy(models.ImageColumns.CreatedAt).Desc(),
).All()
} }
return result, nil return result, nil

View file

@ -3,7 +3,6 @@ package routes
import ( import (
"net/http" "net/http"
"github.com/tigorlazuardi/redmage/api"
"github.com/tigorlazuardi/redmage/pkg/errs" "github.com/tigorlazuardi/redmage/pkg/errs"
"github.com/tigorlazuardi/redmage/pkg/log" "github.com/tigorlazuardi/redmage/pkg/log"
"github.com/tigorlazuardi/redmage/views" "github.com/tigorlazuardi/redmage/views"
@ -15,14 +14,11 @@ func (routes *Routes) PageSubreddits(rw http.ResponseWriter, r *http.Request) {
defer span.End() defer span.End()
c := views.NewContext(routes.Config, r) c := views.NewContext(routes.Config, r)
var params api.ListSubredditsParams
params.FillFromQuery(r.URL.Query())
var data subreddits.Data var data subreddits.Data
var err error data.Params.FillFromQuery(r.URL.Query())
data.Subreddits, err = routes.API.ListSubredditsWithCover(ctx, params) var err error
data.Subreddits, err = routes.API.ListSubredditsWithCover(ctx, data.Params)
if err != nil { if err != nil {
log.New(ctx).Err(err).Error("failed to list subreddits") log.New(ctx).Err(err).Error("failed to list subreddits")
code, message := errs.HTTPMessage(err) code, message := errs.HTTPMessage(err)

View file

@ -0,0 +1,60 @@
package subreddits
import "github.com/tigorlazuardi/redmage/api"
templ Filter(params api.ListSubredditsParams) {
<div
id="filter-bar"
hx-get="/subreddits"
hx-target="#subreddits-list"
hx-select="#subreddits-list"
hx-trigger="change,input delay:300ms"
hx-include="this"
hx-push-url="true"
hx-swap="outerHTML"
class="grid grid-cols-[1fr,4fr] sm:grid-cols-[1fr,4fr,1fr,4fr] items-center gap-4"
>
@searchInput(params)
@nsfwSelect(params)
@orderInput(params)
@sortInput(params)
</div>
}
templ searchInput(params api.ListSubredditsParams) {
<label for="search" class="label">Search</label>
<input
type="text"
id="search"
name="q"
class="input input-bordered"
value={ params.Q }
placeholder="Filter Subreddit"
/>
}
templ nsfwSelect(params api.ListSubredditsParams) {
<label for="nsfw" class="label">NSFW</label>
<select id="nsfw" name="nsfw" class="select select-bordered">
<option value="-1" selected?={ params.NSFW < 0 || params.NSFW > 1 }>*No Filter</option>
<option value="0" selected?={ params.NSFW == 0 }>Hide</option>
<option value="1" selected?={ params.NSFW == 1 }>Show Only</option>
</select>
}
templ orderInput(params api.ListSubredditsParams) {
<label for="order" class="label">Order By</label>
<select id="order" name="order_by" class="select select-bordered">
<option value="updated_at" selected?={ params.OrderBy == "updated_at" }>Latest Update</option>
<option value="created_at" selected?={ params.OrderBy == "created_at" }>Created</option>
<option value="name" selected?={ params.OrderBy == "name" }>Name</option>
</select>
}
templ sortInput(params api.ListSubredditsParams) {
<label for="sort" class="label">Sort</label>
<select id="sort" name="sort" class="select select-bordered">
<option value="desc" selected?={ params.Sort == "desc" }>Descending</option>
<option value="asc" selected?={ params.Sort == "asc" }>Ascending</option>
</select>
}

View file

@ -11,6 +11,7 @@ import "github.com/tigorlazuardi/redmage/views/icons"
type Data struct { type Data struct {
Subreddits api.ListSubredditsResult Subreddits api.ListSubredditsResult
Error string Error string
Params api.ListSubredditsParams
} }
templ View(c *views.Context, data Data) { templ View(c *views.Context, data Data) {
@ -34,17 +35,22 @@ templ Content(c *views.Context, data Data) {
</div> </div>
</div> </div>
<div class="divider"></div> <div class="divider"></div>
if data.Subreddits.Total > 0 {
@Filter(data.Params)
}
<div id="subreddits-list">
if data.Subreddits.Total == 0 { if data.Subreddits.Total == 0 {
<h3>No Subreddits Found</h3> <h3>No Subreddits Found</h3>
<p>Click <a class="text-primary" href="/subreddits/add">here</a> to add a new subreddit.</p> <p>Click <a class="text-primary" href="/subreddits/add">here</a> to add a new subreddit.</p>
} else { } else {
<h2>{ strconv.FormatInt(data.Subreddits.Total, 10) } Subreddits Registered</h2> <h2 class="my-4">{ strconv.FormatInt(data.Subreddits.Total, 10) } Subreddits Registered</h2>
} }
<div class="flex flex-wrap gap-1 justify-around" hx-boost="true"> <div class="flex flex-wrap gap-1 justify-around" hx-boost="true">
for _, subreddit := range data.Subreddits.Data { for _, subreddit := range data.Subreddits.Data {
@SubredditCard(c, subreddit) @SubredditCard(c, subreddit)
} }
</div> </div>
</div>
} }
</main> </main>
} }