wip
This commit is contained in:
57
web/pages/activity.go
Normal file
57
web/pages/activity.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package pages
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
g "maragu.dev/gomponents"
|
||||
h "maragu.dev/gomponents/html"
|
||||
"reichard.io/antholume/pkg/formatters"
|
||||
"reichard.io/antholume/pkg/sliceutils"
|
||||
"reichard.io/antholume/web/components/ui"
|
||||
"reichard.io/antholume/web/models"
|
||||
)
|
||||
|
||||
var _ Page = (*Activity)(nil)
|
||||
|
||||
type Activity struct {
|
||||
Data []models.Activity
|
||||
}
|
||||
|
||||
func (Activity) Route() PageRoute { return ActivityPage }
|
||||
|
||||
func (p Activity) Render() g.Node {
|
||||
return h.Div(
|
||||
h.Class("overflow-x-auto"),
|
||||
h.Div(
|
||||
h.Class("inline-block min-w-full overflow-hidden rounded shadow"),
|
||||
ui.Table(p.buildTableConfig()),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
func (p *Activity) buildTableConfig() ui.TableConfig {
|
||||
return ui.TableConfig{
|
||||
Columns: []string{"Document", "Time", "Duration", "Percent"},
|
||||
Rows: sliceutils.Map(p.Data, toActivityTableRow),
|
||||
}
|
||||
}
|
||||
|
||||
func toActivityTableRow(r models.Activity) ui.TableRow {
|
||||
return ui.TableRow{
|
||||
"Document": ui.TableCell{
|
||||
Value: h.A(
|
||||
h.Href(fmt.Sprintf("./documents/%s", r.ID)),
|
||||
g.Text(fmt.Sprintf("%s - %s", r.Author, r.Title)),
|
||||
),
|
||||
},
|
||||
"Time": ui.TableCell{
|
||||
String: r.StartTime,
|
||||
},
|
||||
"Duration": ui.TableCell{
|
||||
String: formatters.FormatDuration(r.Duration),
|
||||
},
|
||||
"Percent": ui.TableCell{
|
||||
String: fmt.Sprintf("%.2f%%", r.Percentage),
|
||||
},
|
||||
}
|
||||
}
|
||||
129
web/pages/document.go
Normal file
129
web/pages/document.go
Normal file
@@ -0,0 +1,129 @@
|
||||
package pages
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
g "maragu.dev/gomponents"
|
||||
h "maragu.dev/gomponents/html"
|
||||
"reichard.io/antholume/pkg/formatters"
|
||||
"reichard.io/antholume/pkg/ptr"
|
||||
"reichard.io/antholume/pkg/utils"
|
||||
"reichard.io/antholume/web/assets"
|
||||
"reichard.io/antholume/web/components/document"
|
||||
"reichard.io/antholume/web/components/ui"
|
||||
"reichard.io/antholume/web/models"
|
||||
)
|
||||
|
||||
var _ Page = (*Document)(nil)
|
||||
|
||||
type Document struct {
|
||||
Data models.Document
|
||||
Search *models.DocumentMetadata
|
||||
}
|
||||
|
||||
func (Document) Route() PageRoute { return DocumentPage }
|
||||
|
||||
func (p Document) Render() g.Node {
|
||||
return h.Div(
|
||||
h.Class("h-full w-full overflow-scroll bg-white shadow-lg dark:bg-gray-700 rounded dark:text-white p-4"),
|
||||
document.Actions(p.Data),
|
||||
|
||||
// Details
|
||||
h.Div(
|
||||
h.Class("grid sm:grid-cols-2 justify-between gap-3 pb-3"),
|
||||
|
||||
editableKeyValue(
|
||||
p.Data.ID,
|
||||
"Title",
|
||||
p.Data.Title,
|
||||
"title",
|
||||
),
|
||||
editableKeyValue(
|
||||
p.Data.ID,
|
||||
"Author",
|
||||
p.Data.Author,
|
||||
"author",
|
||||
),
|
||||
popoverKeyValue(
|
||||
"Time Read",
|
||||
formatters.FormatDuration(p.Data.TotalTimeRead),
|
||||
"info",
|
||||
p.detailsPopover(),
|
||||
),
|
||||
|
||||
ui.KeyValue(
|
||||
g.Text("Progress"),
|
||||
g.Text(fmt.Sprintf("%.2f%%", p.Data.Percentage)),
|
||||
),
|
||||
ui.KeyValue(
|
||||
g.Text("ISBN-10"),
|
||||
g.Text(utils.FirstNonZero(p.Data.ISBN10, "N/A")),
|
||||
),
|
||||
ui.KeyValue(
|
||||
g.Text("ISBN-13"),
|
||||
g.Text(utils.FirstNonZero(p.Data.ISBN13, "N/A")),
|
||||
),
|
||||
),
|
||||
|
||||
editableKeyValue(
|
||||
p.Data.ID,
|
||||
"Description",
|
||||
p.Data.Description,
|
||||
"description",
|
||||
ui.PopoverConfig{Classes: "w-full"},
|
||||
),
|
||||
|
||||
document.IdentifyPopover(p.Data.ID, p.Search),
|
||||
)
|
||||
}
|
||||
|
||||
func (p *Document) detailsPopover() g.Node {
|
||||
totalTimeLeft := time.Duration((100.0 - p.Data.Percentage) * float64(p.Data.TimePerPercent))
|
||||
percentPerHour := 1.0 / p.Data.TimePerPercent.Hours()
|
||||
return h.Div(
|
||||
statKV("WPM", fmt.Sprint(p.Data.WPM)),
|
||||
statKV("Words", formatters.FormatNumber(ptr.Deref(p.Data.Words))),
|
||||
statKV("Hourly Rate", fmt.Sprintf("%.1f%%", percentPerHour)),
|
||||
statKV("Time Remaining", formatters.FormatDuration(totalTimeLeft)),
|
||||
)
|
||||
}
|
||||
|
||||
func popoverKeyValue(title, value, icon string, popover g.Node, popoverCfg ...ui.PopoverConfig) g.Node {
|
||||
return ui.KeyValue(
|
||||
ui.AnchoredPopover(
|
||||
h.Div(
|
||||
h.Class("inline-flex gap-2 items-center"),
|
||||
h.P(g.Text(title)),
|
||||
ui.SpanButton(assets.Icon(icon, 18), ui.ButtonConfig{Variant: ui.ButtonVariantGhost}),
|
||||
),
|
||||
popover,
|
||||
popoverCfg...,
|
||||
),
|
||||
g.Text(value),
|
||||
)
|
||||
}
|
||||
|
||||
func editableKeyValue(id, title, currentValue, formKey string, popoverCfg ...ui.PopoverConfig) g.Node {
|
||||
currentValue = utils.FirstNonZero(currentValue, "N/A")
|
||||
editPopover := h.Form(
|
||||
h.Class("flex flex-col gap-2"),
|
||||
h.Action(fmt.Sprintf("./%s/edit", id)),
|
||||
h.Method("POST"),
|
||||
h.Textarea(
|
||||
h.ID(formKey),
|
||||
h.Name(formKey),
|
||||
h.Class("p-2 bg-gray-300 text-black dark:bg-gray-700 dark:text-white"),
|
||||
g.Text(currentValue),
|
||||
),
|
||||
ui.FormButton(g.Text("Save"), ""),
|
||||
)
|
||||
return popoverKeyValue(title, currentValue, "edit", editPopover, popoverCfg...)
|
||||
}
|
||||
|
||||
func statKV(title, val string) g.Node {
|
||||
return ui.HKeyValue(
|
||||
h.P(h.Class("text-xs w-24 text-gray-400"), g.Text(title)),
|
||||
h.P(h.Class("text-xs text-nowrap"), g.Text(val)),
|
||||
)
|
||||
}
|
||||
121
web/pages/documents.go
Normal file
121
web/pages/documents.go
Normal file
@@ -0,0 +1,121 @@
|
||||
package pages
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
g "maragu.dev/gomponents"
|
||||
h "maragu.dev/gomponents/html"
|
||||
"reichard.io/antholume/web/assets"
|
||||
"reichard.io/antholume/web/components/document"
|
||||
"reichard.io/antholume/web/components/ui"
|
||||
"reichard.io/antholume/web/models"
|
||||
)
|
||||
|
||||
var _ Page = (*Documents)(nil)
|
||||
|
||||
type Documents struct {
|
||||
Data []models.Document
|
||||
Previous int
|
||||
Next int
|
||||
Limit int
|
||||
}
|
||||
|
||||
func (Documents) Route() PageRoute { return DocumentsPage }
|
||||
|
||||
func (p Documents) Render() g.Node {
|
||||
return g.Group([]g.Node{
|
||||
searchBar(),
|
||||
documentGrid(p.Data),
|
||||
pagination(p.Previous, p.Next, p.Limit),
|
||||
uploadFAB(),
|
||||
})
|
||||
}
|
||||
|
||||
func searchBar() g.Node {
|
||||
return h.Div(
|
||||
h.Class("flex flex-col gap-2 grow p-4 mb-4 rounded shadow-lg bg-white dark:bg-gray-700 text-gray-500 dark:text-white"),
|
||||
h.Form(
|
||||
h.Action("./documents"),
|
||||
h.Method("GET"),
|
||||
h.Class("flex gap-4 flex-col lg:flex-row"),
|
||||
h.Div(
|
||||
h.Class("flex flex-col w-full grow"),
|
||||
h.Div(
|
||||
h.Class("flex relative"),
|
||||
h.Span(
|
||||
h.Class("inline-flex items-center px-3 border-t bg-white border-l border-b border-gray-300 text-gray-500 shadow-sm text-sm"),
|
||||
assets.Icon("search2", 15),
|
||||
),
|
||||
h.Input(
|
||||
h.Type("text"),
|
||||
h.ID("search"),
|
||||
h.Name("search"),
|
||||
h.Class("flex-1 appearance-none rounded-none border border-gray-300 w-full py-2 px-2 bg-white text-gray-700 placeholder-gray-400 shadow-sm text-base focus:outline-none focus:ring-2 focus:ring-purple-600 focus:border-transparent"),
|
||||
h.Placeholder("Search Author / Title"),
|
||||
),
|
||||
),
|
||||
),
|
||||
h.Div(
|
||||
h.Class("lg:w-60"),
|
||||
ui.FormButton(g.Text("Search"), "", ui.ButtonConfig{Variant: ui.ButtonVariantSecondary}),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
func documentGrid(docs []models.Document) g.Node {
|
||||
return h.Div(
|
||||
h.Class("grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3"),
|
||||
g.Map(docs, func(d models.Document) g.Node { return document.Card(d) }),
|
||||
)
|
||||
}
|
||||
|
||||
func pagination(prev, next int, limit int) g.Node {
|
||||
link := func(page int, label string) g.Node {
|
||||
return h.A(
|
||||
h.Href(fmt.Sprintf("./documents?page=%d&limit=%d", page, limit)),
|
||||
h.Class("bg-white shadow-lg dark:bg-gray-600 hover:bg-gray-400 font-medium rounded text-sm text-center p-2 w-24 dark:hover:bg-gray-700 focus:outline-none"),
|
||||
g.Text(label),
|
||||
)
|
||||
}
|
||||
return h.Div(
|
||||
h.Class("w-full flex gap-4 justify-center mt-4 text-black dark:text-white"),
|
||||
g.If(prev > 0, link(prev, "◄")),
|
||||
g.If(next > 0, link(next, "►")),
|
||||
)
|
||||
}
|
||||
|
||||
func uploadFAB() g.Node {
|
||||
return h.Div(
|
||||
h.Class("fixed bottom-6 right-6 rounded-full flex items-center justify-center"),
|
||||
h.Input(h.Type("checkbox"), h.ID("upload-file-button"), h.Class("hidden css-button")),
|
||||
h.Div(
|
||||
h.Class("absolute right-0 z-10 bottom-0 rounded p-4 bg-gray-800 dark:bg-gray-200 text-white dark:text-black w-72 text-sm flex flex-col gap-2"),
|
||||
h.Form(
|
||||
h.Method("POST"),
|
||||
g.Attr("enctype", "multipart/form-data"),
|
||||
h.Action("./documents"),
|
||||
h.Class("flex flex-col gap-2"),
|
||||
h.Input(
|
||||
h.Type("file"),
|
||||
h.Accept(".epub"),
|
||||
h.ID("document_file"),
|
||||
h.Name("document_file"),
|
||||
),
|
||||
ui.FormButton(g.Text("Upload File"), ""),
|
||||
),
|
||||
h.Label(
|
||||
h.For("upload-file-button"),
|
||||
h.Div(
|
||||
h.Class("w-full text-center cursor-pointer font-medium mt-2 px-2 py-1 text-gray-800 bg-gray-500 dark:text-white hover:bg-gray-100 dark:hover:bg-gray-800"),
|
||||
g.Text("Cancel Upload"),
|
||||
),
|
||||
),
|
||||
),
|
||||
h.Label(
|
||||
h.For("upload-file-button"),
|
||||
h.Class("w-16 h-16 bg-gray-800 dark:bg-gray-200 rounded-full flex items-center justify-center opacity-30 hover:opacity-100 transition-all duration-200 cursor-pointer"),
|
||||
assets.Icon("upload", 34),
|
||||
),
|
||||
)
|
||||
}
|
||||
66
web/pages/home.go
Normal file
66
web/pages/home.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package pages
|
||||
|
||||
import (
|
||||
g "maragu.dev/gomponents"
|
||||
h "maragu.dev/gomponents/html"
|
||||
"reichard.io/antholume/database"
|
||||
"reichard.io/antholume/web/components/stats"
|
||||
)
|
||||
|
||||
var _ Page = (*Home)(nil)
|
||||
|
||||
type Home struct {
|
||||
Leaderboard []stats.LeaderboardData
|
||||
Streaks []database.UserStreak
|
||||
DailyStats []database.GetDailyReadStatsRow
|
||||
RecordInfo *database.GetDatabaseInfoRow
|
||||
}
|
||||
|
||||
func (Home) Route() PageRoute { return HomePage }
|
||||
|
||||
func (p Home) Render() g.Node {
|
||||
return h.Div(
|
||||
g.Attr("class", "flex flex-col gap-4"),
|
||||
h.Div(
|
||||
g.Attr("class", "w-full"),
|
||||
h.Div(
|
||||
g.Attr("class", "relative w-full bg-white shadow-lg dark:bg-gray-700 rounded"),
|
||||
h.P(
|
||||
g.Attr("class", "absolute top-3 left-5 text-sm font-semibold border-b border-gray-200 w-max dark:border-gray-500"),
|
||||
g.Text("Daily Read Totals"),
|
||||
),
|
||||
stats.MonthlyChart(p.DailyStats),
|
||||
),
|
||||
),
|
||||
h.Div(
|
||||
g.Attr("class", "grid grid-cols-2 gap-4 md:grid-cols-4"),
|
||||
stats.InfoCard(stats.InfoCardData{
|
||||
Title: "Documents",
|
||||
Size: p.RecordInfo.DocumentsSize,
|
||||
Link: "./documents",
|
||||
}),
|
||||
stats.InfoCard(stats.InfoCardData{
|
||||
Title: "Activity Records",
|
||||
Size: p.RecordInfo.ActivitySize,
|
||||
Link: "./activity",
|
||||
}),
|
||||
stats.InfoCard(stats.InfoCardData{
|
||||
Title: "Progress Records",
|
||||
Size: p.RecordInfo.ProgressSize,
|
||||
Link: "./progress",
|
||||
}),
|
||||
stats.InfoCard(stats.InfoCardData{
|
||||
Title: "Devices",
|
||||
Size: p.RecordInfo.DevicesSize,
|
||||
}),
|
||||
),
|
||||
h.Div(
|
||||
g.Attr("class", "grid grid-cols-1 gap-4 md:grid-cols-2"),
|
||||
g.Map(p.Streaks, stats.StreakCard),
|
||||
),
|
||||
h.Div(
|
||||
g.Attr("class", "grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3"),
|
||||
g.Map(p.Leaderboard, stats.LeaderboardCard),
|
||||
),
|
||||
)
|
||||
}
|
||||
42
web/pages/page.go
Normal file
42
web/pages/page.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package pages
|
||||
|
||||
import (
|
||||
g "maragu.dev/gomponents"
|
||||
)
|
||||
|
||||
type PageRoute string
|
||||
|
||||
const (
|
||||
HomePage PageRoute = "home"
|
||||
DocumentPage PageRoute = "document"
|
||||
DocumentsPage PageRoute = "documents"
|
||||
ProgressPage PageRoute = "progress"
|
||||
ActivityPage PageRoute = "activity"
|
||||
SearchPage PageRoute = "search"
|
||||
AdminGeneralPage PageRoute = "admin-general"
|
||||
AdminImportPage PageRoute = "admin-import"
|
||||
AdminUsersPage PageRoute = "admin-users"
|
||||
AdminLogsPage PageRoute = "admin-logs"
|
||||
)
|
||||
|
||||
var pageTitleMap = map[PageRoute]string{
|
||||
HomePage: "Home",
|
||||
DocumentPage: "Document",
|
||||
DocumentsPage: "Documents",
|
||||
ProgressPage: "Progress",
|
||||
ActivityPage: "Activity",
|
||||
SearchPage: "Search",
|
||||
AdminGeneralPage: "Admin - General",
|
||||
AdminImportPage: "Admin - Import",
|
||||
AdminUsersPage: "Admin - Users",
|
||||
AdminLogsPage: "Admin - Logs",
|
||||
}
|
||||
|
||||
func (p PageRoute) Title() string {
|
||||
return pageTitleMap[p]
|
||||
}
|
||||
|
||||
type Page interface {
|
||||
Route() PageRoute
|
||||
Render() g.Node
|
||||
}
|
||||
56
web/pages/progress.go
Normal file
56
web/pages/progress.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package pages
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
g "maragu.dev/gomponents"
|
||||
h "maragu.dev/gomponents/html"
|
||||
"reichard.io/antholume/pkg/sliceutils"
|
||||
"reichard.io/antholume/web/components/ui"
|
||||
"reichard.io/antholume/web/models"
|
||||
)
|
||||
|
||||
var _ Page = (*Progress)(nil)
|
||||
|
||||
type Progress struct {
|
||||
Data []models.Progress
|
||||
}
|
||||
|
||||
func (Progress) Route() PageRoute { return ProgressPage }
|
||||
|
||||
func (p Progress) Render() g.Node {
|
||||
return h.Div(
|
||||
h.Class("overflow-x-auto"),
|
||||
h.Div(
|
||||
h.Class("inline-block min-w-full overflow-hidden rounded shadow"),
|
||||
ui.Table(p.buildTableConfig()),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
func (p *Progress) buildTableConfig() ui.TableConfig {
|
||||
return ui.TableConfig{
|
||||
Columns: []string{"Document", "Device Name", "Percentage", "Created At"},
|
||||
Rows: sliceutils.Map(p.Data, toProgressTableRow),
|
||||
}
|
||||
}
|
||||
|
||||
func toProgressTableRow(r models.Progress) ui.TableRow {
|
||||
return ui.TableRow{
|
||||
"Document": ui.TableCell{
|
||||
Value: h.A(
|
||||
h.Href(fmt.Sprintf("./documents/%s", r.ID)),
|
||||
g.Text(fmt.Sprintf("%s - %s", r.Author, r.Title)),
|
||||
),
|
||||
},
|
||||
"Device Name": ui.TableCell{
|
||||
String: r.DeviceName,
|
||||
},
|
||||
"Percentage": ui.TableCell{
|
||||
String: fmt.Sprintf("%.2f%%", r.Percentage),
|
||||
},
|
||||
"Created At": ui.TableCell{
|
||||
String: r.CreatedAt,
|
||||
},
|
||||
}
|
||||
}
|
||||
129
web/pages/search.go
Normal file
129
web/pages/search.go
Normal file
@@ -0,0 +1,129 @@
|
||||
package pages
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
g "maragu.dev/gomponents"
|
||||
h "maragu.dev/gomponents/html"
|
||||
|
||||
"reichard.io/antholume/pkg/sliceutils"
|
||||
"reichard.io/antholume/pkg/utils"
|
||||
"reichard.io/antholume/search"
|
||||
"reichard.io/antholume/web/assets"
|
||||
"reichard.io/antholume/web/components/ui"
|
||||
"reichard.io/antholume/web/models"
|
||||
)
|
||||
|
||||
var _ Page = (*Search)(nil)
|
||||
|
||||
type Search struct {
|
||||
Query string
|
||||
Source search.Source
|
||||
Results []models.SearchResult
|
||||
Error string
|
||||
}
|
||||
|
||||
func (Search) Route() PageRoute { return SearchPage }
|
||||
|
||||
func (p Search) Render() g.Node {
|
||||
return h.Div(
|
||||
h.Class("flex flex-col gap-4"),
|
||||
h.Div(
|
||||
h.Class("flex flex-col gap-2 p-4 rounded shadow-lg bg-white dark:bg-gray-700"),
|
||||
h.Form(
|
||||
h.Class("flex gap-4 flex-col lg:flex-row"),
|
||||
h.Action("./search"),
|
||||
h.Div(
|
||||
h.Class("flex w-full"),
|
||||
h.Span(
|
||||
h.Class("inline-flex items-center px-3 border-t bg-white border-l border-b border-gray-300 text-gray-500 shadow-sm text-sm"),
|
||||
assets.Icon("search2", 15),
|
||||
),
|
||||
h.Input(
|
||||
h.Type("text"),
|
||||
h.ID("query"),
|
||||
h.Name("query"),
|
||||
h.Value(p.Query),
|
||||
h.Class("flex-1 appearance-none rounded-none border border-gray-300 w-full py-2 px-4 bg-white text-gray-700 placeholder-gray-400 shadow-sm text-base focus:outline-none focus:ring-2 focus:ring-purple-600 focus:border-transparent"),
|
||||
h.Placeholder("Query"),
|
||||
),
|
||||
),
|
||||
h.Div(
|
||||
h.Class("flex relative min-w-[12em]"),
|
||||
h.Span(
|
||||
h.Class("inline-flex items-center px-3 border-t bg-white border-l border-b border-gray-300 text-gray-500 shadow-sm text-sm"),
|
||||
assets.Icon("documents", 15),
|
||||
),
|
||||
h.Select(
|
||||
h.Class("flex-1 appearance-none rounded-none border border-gray-300 w-full py-2 px-4 bg-white text-gray-700 placeholder-gray-400 shadow-sm text-base focus:outline-none focus:ring-2 focus:ring-purple-600 focus:border-transparent"),
|
||||
h.ID("source"),
|
||||
h.Name("source"),
|
||||
h.Option(
|
||||
h.Value("LibGen"),
|
||||
g.If(p.Source == search.SourceLibGen, h.Selected()),
|
||||
g.Text("Library Genesis"),
|
||||
),
|
||||
h.Option(
|
||||
h.Value("Annas Archive"),
|
||||
g.If(p.Source == search.SourceAnnasArchive, h.Selected()),
|
||||
g.Text("Annas Archive"),
|
||||
),
|
||||
),
|
||||
),
|
||||
h.Div(
|
||||
h.Class("lg:w-60"),
|
||||
ui.FormButton(
|
||||
g.Text("Search"),
|
||||
"",
|
||||
ui.ButtonConfig{Variant: ui.ButtonVariantSecondary},
|
||||
),
|
||||
),
|
||||
),
|
||||
g.If(
|
||||
p.Error != "",
|
||||
h.Span(h.Class("text-red-400 text-xs"), g.Text(p.Error)),
|
||||
),
|
||||
),
|
||||
h.Div(
|
||||
h.Class("inline-block min-w-full overflow-hidden rounded shadow"),
|
||||
ui.Table(
|
||||
ui.TableConfig{
|
||||
Columns: []string{"", "Document", "Series", "Type", "Size", "Date"},
|
||||
Rows: p.tableRows(),
|
||||
},
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
func (p Search) tableRows() []ui.TableRow {
|
||||
return sliceutils.Map(p.Results, func(r models.SearchResult) ui.TableRow {
|
||||
return ui.TableRow{
|
||||
"": ui.TableCell{
|
||||
Value: h.Form(
|
||||
h.Action("./search"),
|
||||
h.Method("POST"),
|
||||
h.Input(h.Type("hidden"), h.Name("source"), h.Value(string(p.Source))),
|
||||
h.Input(h.Type("hidden"), h.Name("title"), h.Value(r.Title)),
|
||||
h.Input(h.Type("hidden"), h.Name("author"), h.Value(r.Author)),
|
||||
ui.FormButton(assets.Icon("download", 24), "", ui.ButtonConfig{Variant: ui.ButtonVariantGhost}),
|
||||
),
|
||||
},
|
||||
"Document": ui.TableCell{
|
||||
String: fmt.Sprintf("%s - %s", r.Author, r.Title),
|
||||
},
|
||||
"Series": ui.TableCell{
|
||||
String: utils.FirstNonZero(r.Series, "N/A"),
|
||||
},
|
||||
"Type": ui.TableCell{
|
||||
String: utils.FirstNonZero(r.FileType, "N/A"),
|
||||
},
|
||||
"Size": ui.TableCell{
|
||||
String: utils.FirstNonZero(r.FileSize, "N/A"),
|
||||
},
|
||||
"Date": ui.TableCell{
|
||||
String: utils.FirstNonZero(r.UploadDate, "N/A"),
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user