view(subreddits-list): added filter bar
This commit is contained in:
parent
d2b81567e3
commit
4f3ba41237
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
60
views/subreddits/filter.templ
Normal file
60
views/subreddits/filter.templ
Normal 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>
|
||||||
|
}
|
|
@ -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,16 +35,21 @@ templ Content(c *views.Context, data Data) {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="divider"></div>
|
<div class="divider"></div>
|
||||||
if data.Subreddits.Total == 0 {
|
if data.Subreddits.Total > 0 {
|
||||||
<h3>No Subreddits Found</h3>
|
@Filter(data.Params)
|
||||||
<p>Click <a class="text-primary" href="/subreddits/add">here</a> to add a new subreddit.</p>
|
|
||||||
} else {
|
|
||||||
<h2>{ strconv.FormatInt(data.Subreddits.Total, 10) } Subreddits Registered</h2>
|
|
||||||
}
|
}
|
||||||
<div class="flex flex-wrap gap-1 justify-around" hx-boost="true">
|
<div id="subreddits-list">
|
||||||
for _, subreddit := range data.Subreddits.Data {
|
if data.Subreddits.Total == 0 {
|
||||||
@SubredditCard(c, subreddit)
|
<h3>No Subreddits Found</h3>
|
||||||
|
<p>Click <a class="text-primary" href="/subreddits/add">here</a> to add a new subreddit.</p>
|
||||||
|
} else {
|
||||||
|
<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">
|
||||||
|
for _, subreddit := range data.Subreddits.Data {
|
||||||
|
@SubredditCard(c, subreddit)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</main>
|
</main>
|
||||||
|
|
Loading…
Reference in a new issue