Merge pull request 'features/templ-components' (#3) from features/templ-components into main

Reviewed-on: #3
This commit is contained in:
jtom38 2024-07-13 10:40:27 -07:00
commit 209a28501c
50 changed files with 661 additions and 106 deletions

View File

@ -16,6 +16,7 @@ const (
type Sources interface { type Sources interface {
ListAll(jwt string, page int) (domain.SourcesResponse, error) ListAll(jwt string, page int) (domain.SourcesResponse, error)
GetById(jwt string, id int64) (domain.SourcesResponse, error) GetById(jwt string, id int64) (domain.SourcesResponse, error)
NewRss(jwt, name, url, sourceType string) (domain.SourcesResponse, error)
} }
type sourceClient struct { type sourceClient struct {
@ -53,7 +54,7 @@ func (c sourceClient) ListAll(jwt string, page int) (domain.SourcesResponse, err
return bind, err return bind, err
} }
if (resp.StatusCode != 200) { if resp.StatusCode != 200 {
return bind, errors.New(bind.Message) return bind, errors.New(bind.Message)
} }
@ -69,9 +70,33 @@ func (c sourceClient) GetById(jwt string, id int64) (domain.SourcesResponse, err
return bind, err return bind, err
} }
if (statusCode != 200) { if statusCode != 200 {
return bind, errors.New(bind.Message) return bind, errors.New(bind.Message)
} }
return bind, nil return bind, nil
} }
func (c sourceClient) NewRss(jwt, name, url, sourceType string) (domain.SourcesResponse, error) {
param := domain.NewSourceParamRequest{
Name: name,
Url: url,
Tags: "",
}
bind := domain.SourcesResponse{}
var endpoint string
if sourceType == domain.SourceCollectorRss {
endpoint = fmt.Sprintf("%s/%s/new/rss", c.serverAddress, SourcesBaseRoute)
}
statusCode, err := PostBodyUrlAuthorized(c.client, endpoint, jwt, param, &bind)
if err != nil {
return bind, err
}
if statusCode != 200 {
return bind, errors.New("got the wrong status code back from the API")
}
return bind, nil
}

View File

@ -58,6 +58,35 @@ func PostUrlAuthorized(client http.Client, endpoint, jwtToken string, t any) err
return nil return nil
} }
func PostBodyUrlAuthorized(client http.Client, endpoint, jwtToken string, body any, t any) (int, error) {
jsonBody, err := json.Marshal(body)
if err != nil {
return 0, err
}
req, err := http.NewRequest(http.MethodPost, endpoint, bytes.NewBuffer(jsonBody))
if err != nil {
return 0, err
}
req.Header.Add(HeaderAuthorization, fmt.Sprintf("%s %s", "Bearer", jwtToken))
req.Header.Add(HeaderContentType, ApplicationJson)
//response, err := http.Post(endpoint, ApplicationJson, bytes.NewBuffer(jsonBody))
response, err := client.Do(req)
if err != nil {
return response.StatusCode, err
}
defer response.Body.Close()
decoder := json.NewDecoder(response.Body)
err = decoder.Decode(&t)
if err != nil {
return response.StatusCode, err
}
return response.StatusCode, nil
}
func PostBodyUrl(client http.Client, endpoint string, body any, t any) error { func PostBodyUrl(client http.Client, endpoint string, body any, t any) error {
jsonBody, err := json.Marshal(body) jsonBody, err := json.Marshal(body)
if err != nil { if err != nil {
@ -79,6 +108,22 @@ func PostBodyUrl(client http.Client, endpoint string, body any, t any) error {
return nil return nil
} }
func PostQuery(client http.Client, endpoint string, t any) (int, error) {
response, err := http.Post(endpoint, ApplicationJson, nil)
if err != nil {
return response.StatusCode, err
}
defer response.Body.Close()
decoder := json.NewDecoder(response.Body)
err = decoder.Decode(&t)
if err != nil {
return response.StatusCode, err
}
return response.StatusCode, nil
}
func Get(client http.Client, endpoint, jwt string, t any) (int, error) { func Get(client http.Client, endpoint, jwt string, t any) (int, error) {
req, err := http.NewRequest(http.MethodGet, endpoint, nil) req, err := http.NewRequest(http.MethodGet, endpoint, nil)
if err != nil { if err != nil {

View File

@ -0,0 +1,8 @@
package bulma
// Simple spacer that accepts children
templ Block() {
<div class="block">
{ children... }
</div>
}

View File

@ -0,0 +1,30 @@
package bulma
// This creates a button that accepts children under it.
templ Button() {
<button type="button" class={ "button" }>
{ children... }
</button>
}
// Used to create a button and lets you define the color.
// Accepts children.
templ ButtonColor(color string) {
<button type="button" class={ "button", color }>
{ children... }
</button>
}
templ ButtonNewTab(url, text string) {
<button type="button" class="button">
<a href={ templ.SafeURL(url) } target="_blank" rel="noopener noreferrer">{ text }</a>
</button>
}
templ ALink(url, title string) {
<a href={ templ.SafeURL(url) }>{ title }</a>
}
templ ANewTab(url, text string) {
<a href={ templ.SafeURL(url) } target="_blank" rel="noopener noreferrer">{ text }</a>
}

View File

@ -0,0 +1,19 @@
package example
import "git.jamestombleson.com/jtom38/newsbot-portal/components/bulma"
import "git.jamestombleson.com/jtom38/newsbot-portal/components/bulma/html"
import "git.jamestombleson.com/jtom38/newsbot-portal/components/bulma/layout"
templ example() {
@html.Doctype()
@html.New("en"){
@html.NewHeader(){
@bulma.UseBulmaCdn()
}
@html.NewBody() {
@layout.Container(bulma.BreakpointDefault) {
{ children... }
}
}
}
}

View File

@ -0,0 +1 @@
package example

View File

@ -0,0 +1,8 @@
package form
// Div container to add a input field to.
templ Control() {
<div class="control">
{ children... }
</div>
}

View File

@ -0,0 +1,8 @@
package form
// This creates a field that you can add a Label, Control or Input object.
templ Field() {
<div class="field">
{ children... }
</div>
}

View File

@ -0,0 +1,19 @@
package form
templ TextInput(id, fieldType, placeholder string) {
<input class={ "input" } name={ id } id={ id } type={ fieldType } placeholder={ placeholder }/>
}
templ TextInputColor(color, id, fieldType, placeholder string) {
if color == "" {
<input class={ "input" } name={ id } id={ id } type={ fieldType } placeholder={ placeholder }/>
} else {
<input class={ "input", color } name={ id } id={ id } type={ fieldType } placeholder={ placeholder }/>
}
}
templ Checkbox(text, id string) {
<label class="checkbox">
<input type="checkbox" id={ id }/> { text }
</label>
}

View File

@ -0,0 +1,6 @@
package form
// This will create a small bit of text to add context to the form.
templ Label(text string) {
<label class="label">{ text }</label>
}

View File

@ -0,0 +1,13 @@
package form
type NewParam struct {
HxPost string
}
templ New(param NewParam) {
if param.HxPost != "" {
<form hx-post={ param.HxPost }>
{ children... }
</form>
}
}

View File

@ -0,0 +1,33 @@
package form
templ SelectOne(color string, isRound bool) {
if isRound {
<div class="select is-round">
<select>
{ children... }
</select>
</div>
} else {
<div class="select">
<select>
{ children... }
</select>
</div>
}
}
templ SelectOneItem(name string) {
<option>{ name }</option>
}
templ SelectMany(howManySelectable int, color string, isRound bool) {
<div class="select is-multiple">
<select multiple size="{ howManySelectable }">
{ children... }
</select>
</div>
}
templ SelectManyItem(name string) {
<option value={ name }>{ name }</option>
}

View File

@ -0,0 +1,5 @@
package form
templ Submit(text, color string) {
<button type="submit" class={ "button", color }>{ text }</button>
}

View File

@ -0,0 +1,5 @@
package form
templ TextArea(id, placeholder, color string) {
<textarea class={ "textarea", color } id={ id } placeholder={ placeholder }/>
}

View File

@ -0,0 +1,8 @@
package form
const (
InputTypeText = "text"
InputTypePassword = "password"
InputTypeEmail = "email"
InputTypePhoneNumber = "tel"
)

View File

@ -0,0 +1,5 @@
package html
templ Br(){
<br/>
}

View File

@ -0,0 +1,9 @@
package html
templ ALink(url, title string) {
<a href={ templ.SafeURL(url) }>{ title }</a>
}
templ ANewTab(url, text string) {
<a href={ templ.SafeURL(url) } target="_blank" rel="noopener noreferrer">{ text }</a>
}

View File

@ -0,0 +1,26 @@
package html
templ Doctype() {
<!DOCTYPE html>
}
// Creates <html> that accepts children
templ New(lang string) {
<html lang={ lang }>
{ children... }
</html>
}
// Creates <head> that accepts children
templ NewHeader() {
<head>
{ children... }
</head>
}
// Creates <body> that accepts children
templ NewBody() {
<body>
{ children... }
</body>
}

View File

@ -0,0 +1,7 @@
package layout
templ Container(breakpoint string) {
<div class={ "container", breakpoint }>
{ children... }
</div>
}

View File

@ -0,0 +1,37 @@
package layout
templ Hero(title, subtitle string) {
<section class="hero">
<div class="hero-body">
<p class="title">{ title }</p>
<p class="subtitle">{ subtitle }</p>
</div>
</section>
}
templ HeroSize(title, subtitle, size string) {
<section class={ "hero", size }>
<div class="hero-body">
<p class="title">{ title }</p>
<p class="subtitle">{ subtitle }</p>
</div>
</section>
}
templ HeroColor(title, subtitle, color string) {
<section class={ "hero", color }>
<div class="hero-body">
<p class="title">{ title }</p>
<p class="subtitle">{ subtitle }</p>
</div>
</section>
}
templ HeroColorSize(title, subtitle, color, size string) {
<section class={ "hero", color, size }>
<div class="hero-body">
<p class="title">{ title }</p>
<p class="subtitle">{ subtitle }</p>
</div>
</section>
}

View File

@ -0,0 +1,9 @@
package bulma
// Creates a <section> object thats good to break up a page of content.
templ Section(title, subtitle string) {
<section class="section">
<h1 class="title">{ title }</h1>
<h2 class="subtitle">{ subtitle }</h2>
</section>
}

View File

@ -0,0 +1,7 @@
package bulma
templ Notification(message, color string) {
<div class={ "notification", color }>
{ message }
</div>
}

View File

@ -0,0 +1,5 @@
package bulma
templ UseBulmaCdn() {
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.1/css/bulma.min.css"/>
}

View File

@ -0,0 +1,37 @@
package bulma
// Creates a <table> that accepts children
templ Table(){
<table class="table">
{ children... }
</table>
}
// Creates a <thead> that accepts children.
templ TableHeader() {
<thead>{ children... }</thead>
}
// Creates a <tf> that accepts children.
templ TableRow() {
<tr>{ children... }</tr>
}
// Creates a <th> and writes the given value
templ TableHeaderData(value string) {
<th>{ value }</th>
}
// Creates a <th> that allows you to also add a tooltip value
templ TableHeaderDataToolTip(value, tooltip string) {
<th><abbr title={ tooltip }>{ value }</abbr></th>
}
// Creates a <td> that accepts children.
templ TableDataChildren() {
<td>{ children... }</td>
}
templ TableData(value string) {
<td>{ value }</td>
}

View File

@ -0,0 +1,13 @@
package bulma
templ Tag(message string) {
<span class={ "tag" }>{ message }</span>
}
templ TagColor(message, color string) {
<span class={ "tag", color }>{ message }</span>
}
templ TagColorSize(message, color, size string) {
<span class={ "tag", color, size }>{ message }</span>
}

View File

@ -0,0 +1,49 @@
package bulma
templ Title(message string) {
<h1 class="title">{ message }</h1>
}
templ Subitle(message string) {
<h2 class="subtitle">{ message }</h2>
}
templ H1(message string, isSubtitle bool) {
if isSubtitle {
<h1 class="subtitle is-1">{ message }</h1>
} else {
<h1 class="title is-1">{ message }</h1>
}
}
templ H2(message string, isSubtitle bool) {
if isSubtitle {
<h2 class="subtitle is-2">{ message }</h2>
} else {
<h2 class="title is-2">{ message }</h2>
}
}
templ H3(message string, isSubtitle bool) {
if isSubtitle {
<h3 class="subtitle is-3">{ message }</h3>
} else {
<h3 class="title is-3">{ message }</h3>
}
}
templ H4(message string, isSubtitle bool) {
if isSubtitle {
<h4 class="subtitle is-4">{ message }</h4>
} else {
<h4 class="title is-4">{ message }</h4>
}
}
templ H5(message string, isSubtitle bool) {
if isSubtitle {
<h5 class="subtitle is-5">{ message }</h5>
} else {
<h5 class="title is-5">{ message }</h5>
}
}

20
components/bulma/util.go Normal file
View File

@ -0,0 +1,20 @@
package bulma
const (
ColorPrimary = "is-primary"
ColorInfo = "is-info"
ColorLink = "is-link"
ColorWarning = "is-warning"
ColorSuccess = "is-success"
ColorError = "is-error"
SizeNormal = "is-normal"
SizeMedium = "is-medium"
SizeLarge = "is-large"
BreakpointDefault = ""
BreakpointWidescreen = "is-widescreen"
BreakpointFullHd = "is-fullhd"
BreakpointMaxDesktop = "is-max-desktop"
BreakpointMaxWidescreen = "is-max-widescreen"
)

2
go.mod
View File

@ -3,7 +3,7 @@ module git.jamestombleson.com/jtom38/newsbot-portal
go 1.22.1 go 1.22.1
require ( require (
git.jamestombleson.com/jtom38/newsbot-api v0.0.0-20240603002809-9237369e5a76 git.jamestombleson.com/jtom38/newsbot-api v0.0.0-20240710142335-56199a795a2b // indirect
github.com/a-h/templ v0.2.747 github.com/a-h/templ v0.2.747
github.com/golang-jwt/jwt/v5 v5.2.1 github.com/golang-jwt/jwt/v5 v5.2.1
github.com/joho/godotenv v1.5.1 github.com/joho/godotenv v1.5.1

2
go.sum
View File

@ -1,5 +1,7 @@
git.jamestombleson.com/jtom38/newsbot-api v0.0.0-20240603002809-9237369e5a76 h1:B9t5fcfVerMjqnXXPUmYwdmUk76EoEL8x9IRehqg2c4= git.jamestombleson.com/jtom38/newsbot-api v0.0.0-20240603002809-9237369e5a76 h1:B9t5fcfVerMjqnXXPUmYwdmUk76EoEL8x9IRehqg2c4=
git.jamestombleson.com/jtom38/newsbot-api v0.0.0-20240603002809-9237369e5a76/go.mod h1:A3UdJyQ/IEy3utEwJiC4nbi0ohfgrUNRLTei2iZhLLA= git.jamestombleson.com/jtom38/newsbot-api v0.0.0-20240603002809-9237369e5a76/go.mod h1:A3UdJyQ/IEy3utEwJiC4nbi0ohfgrUNRLTei2iZhLLA=
git.jamestombleson.com/jtom38/newsbot-api v0.0.0-20240710142335-56199a795a2b h1:XAXD6OSFDzrJ2O1+wma/vnYfLJdcQZRD6NFjfjxjKv4=
git.jamestombleson.com/jtom38/newsbot-api v0.0.0-20240710142335-56199a795a2b/go.mod h1:A3UdJyQ/IEy3utEwJiC4nbi0ohfgrUNRLTei2iZhLLA=
github.com/a-h/templ v0.2.747 h1:D0dQ2lxC3W7Dxl6fxQ/1zZHBQslSkTSvl5FxP/CfdKg= github.com/a-h/templ v0.2.747 h1:D0dQ2lxC3W7Dxl6fxQ/1zZHBQslSkTSvl5FxP/CfdKg=
github.com/a-h/templ v0.2.747/go.mod h1:69ObQIbrcuwPCU32ohNaWce3Cb7qM5GMiqN1K+2yop4= github.com/a-h/templ v0.2.747/go.mod h1:69ObQIbrcuwPCU32ohNaWce3Cb7qM5GMiqN1K+2yop4=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=

View File

@ -36,5 +36,5 @@ func (h *Handler) ArticlesList(c echo.Context) error {
} }
return Render(c, http.StatusOK, articles.List(vm)) return Render(c, http.StatusOK, articles.ListArticlesTable(vm))
} }

View File

@ -65,14 +65,15 @@ func NewServer(ctx context.Context, configs config.Configs, apiClient apiclient.
//sources.Use(ValidateJwtMiddleware(configs.JwtSecret)) //sources.Use(ValidateJwtMiddleware(configs.JwtSecret))
sources.GET("", s.ListAllSources) sources.GET("", s.ListAllSources)
sources.GET("/add", s.AddSource) sources.GET("/add", s.AddSource)
sources.POST("/add", s.AddSourceAfter)
users := router.Group("/users") users := router.Group("/users")
users.GET("/login", s.UserLogin) users.GET("/login", s.UserLogin)
users.POST("/login", s.UserAfterLogin) users.POST("/login", s.UserAfterLogin)
users.GET("/signup", s.UserSignUp) users.GET("/signup", s.UserSignUp)
users.POST("/signup", s.UserAfterSignUp) users.POST("/signup", s.UserAfterSignUp)
users.Use(ValidateJwtMiddleware(configs.JwtSecret))
users.GET("/logout", s.UsersLogout) users.GET("/logout", s.UsersLogout)
users.Use(ValidateJwtMiddleware(configs.JwtSecret))
users.GET("/profile", s.UserProfile) users.GET("/profile", s.UserProfile)
s.Router = router s.Router = router

View File

@ -36,3 +36,13 @@ func (h *Handler) AddSource(c echo.Context) error {
return Render(c, http.StatusOK, sources.Add(models.AddSourcePayloadModel{})) return Render(c, http.StatusOK, sources.Add(models.AddSourcePayloadModel{}))
} }
func (h *Handler) AddSourceAfter(c echo.Context) error {
name := c.FormValue("name")
url := c.FormValue("url")
resp, err := h.api.Sources.NewRss(GetJwtToken(c), name, url, "rss")
if err != nil {
return Render(c, http.StatusOK, sources.AddAfter(err.Error(), true))
}
return Render(c, http.StatusOK, sources.AddAfter(resp.Message, false))
}

View File

@ -11,7 +11,7 @@ import (
) )
func (h *Handler) UserLogin(c echo.Context) error { func (h *Handler) UserLogin(c echo.Context) error {
return Render(c, http.StatusOK, users.Login()) return Render(c, http.StatusOK, users.LoginNew())
} }
func (h *Handler) UserAfterLogin(c echo.Context) error { func (h *Handler) UserAfterLogin(c echo.Context) error {

View File

@ -1,17 +1,16 @@
package articles package articles
import ( import (
"git.jamestombleson.com/jtom38/newsbot-portal/internal/views/layout" "git.jamestombleson.com/jtom38/newsbot-portal/components/bulma"
"git.jamestombleson.com/jtom38/newsbot-portal/internal/models" "git.jamestombleson.com/jtom38/newsbot-portal/internal/models"
"git.jamestombleson.com/jtom38/newsbot-portal/internal/views/bulma" "git.jamestombleson.com/jtom38/newsbot-portal/internal/views/layout"
) )
templ List(model models.ListArticlesViewModel) { templ List(model models.ListArticlesViewModel) {
@layout.WithTemplate() { @layout.WithTemplate() {
@filterBar() @filterBar()
for _, item := range model.Items { for _, item := range model.Items {
@bulma.ArticleCardWithThumbnail(item.Article.Title, item.Article.Thumbnail, item.Article.Url, item.Article.PubDate.String(), item.Source.DisplayName ) @bulma.ArticleCardWithThumbnail(item.Article.Title, item.Article.Thumbnail, item.Article.Url, item.Article.PubDate.String(), item.Source.DisplayName)
} }
} }
} }

View File

@ -0,0 +1,32 @@
package articles
import (
"git.jamestombleson.com/jtom38/newsbot-portal/components/bulma"
"git.jamestombleson.com/jtom38/newsbot-portal/internal/models"
"git.jamestombleson.com/jtom38/newsbot-portal/internal/views/layout"
)
templ ListArticlesTable(model models.ListArticlesViewModel) {
@layout.WithTemplate() {
@bulma.Table() {
@bulma.TableHeader() {
@bulma.TableRow() {
@bulma.TableHeaderData("Title")
@bulma.TableHeaderData("Source")
@bulma.TableHeaderData("View")
}
}
for _, item := range model.Items {
@bulma.TableRow() {
@bulma.TableData(item.Article.Title)
@bulma.TableData(item.Source.DisplayName)
@bulma.TableDataChildren() {
@bulma.Button() {
@bulma.ANewTab(item.Article.Url, "View")
}
}
}
}
}
}
}

View File

@ -1,10 +0,0 @@
package bulma
templ Hero(title, subtitle string) {
<section class="hero">
<div class="hero-body">
<p class="title">{ title }</p>
<p class="subtitle">{ subtitle }</p>
</div>
</section>
}

View File

@ -1,11 +0,0 @@
package bulma
templ Section(title, subtitle string) {
<section class="section">
<h1 class="title">Section</h1>
<h2 class="subtitle">
A simple container to divide your page into <strong>sections</strong>, like
the one you're currently reading.
</h2>
</section>
}

View File

@ -1,15 +1,15 @@
package home package home
import "git.jamestombleson.com/jtom38/newsbot-portal/internal/views/layout" import "git.jamestombleson.com/jtom38/newsbot-portal/internal/views/layout"
import "git.jamestombleson.com/jtom38/newsbot-portal/components/bulma"
templ About() { templ About() {
@layout.WithTemplate(){ @layout.WithTemplate(){
<h1 class="title"> About this project</h1> @bulma.Title("About this project")
@bulma.Block() {
<section class="section">
Newsbot started a small project to send out notifications to discord servers. Newsbot started a small project to send out notifications to discord servers.
I wanted to be able to keep the small communitiy aware of new posts about a game we all played. I wanted to be able to keep the small communitiy aware of new posts about a game we all played.
That feature still exists because I think that keeping a communitiy aware and engaged is important and not everyone follows the same news. That feature still exists because I think that keeping a communitiy aware and engaged is important and not everyone follows the same news.
</section> }
} }
} }

View File

@ -1,32 +1,27 @@
package home package home
import "git.jamestombleson.com/jtom38/newsbot-portal/internal/views/layout" import (
import "git.jamestombleson.com/jtom38/newsbot-portal/internal/views/bulma" b "git.jamestombleson.com/jtom38/newsbot-portal/components/bulma"
bl "git.jamestombleson.com/jtom38/newsbot-portal/components/bulma/layout"
"git.jamestombleson.com/jtom38/newsbot-portal/internal/views/layout"
)
templ Index() { templ Index() {
@layout.WithTemplate() { @layout.WithTemplate() {
@bulma.Hero("Welcome to Newsbot!", "Your new home for your news.") @bl.Hero("Welcome to Newsbot!", "Your new home for your news.")
@b.Block() {
<section class="section"> News bot is a self hostable solution to aggregating your news.
<p> You can run `Newsbot` as an API or interact with it with this site.
News bot is a self hostable solution to aggregating your news. }
You can run `Newsbot` as an API or interact with it with this site. @b.H2("Why Newsbot", false)
</p> @b.Block() {
</section> I started to build this tool to help me avoid sitting on the big platform websites.
I wanted a tool that would work for me, not them.
This tool started as a notification system that would let me redirect RSS posts over to Discord servers.
<div class="block"> It still has those roots but now its starting to scale up to a full Aggregation platform.
<h2 class="title">Why Newsbot</h2> }
I started to build this tool to help me avoid sitting on the big platform websites. @b.Block() {
I wanted a tool that would work for me, not them. This project is a passion project of mine as I
}
This tool started as a notification system that would let me redirect RSS posts over to Discord servers.
It still has those roots but now its starting to scale up to a full Aggregation platform.
</div>
<p>
This project is a passion project of mine as I
</p>
} }
} }

View File

@ -1,7 +1,9 @@
package layout package layout
import "git.jamestombleson.com/jtom38/newsbot-portal/components/bulma"
templ header() { templ header() {
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.0/css/bulma.min.css"/> @bulma.UseBulmaCdn()
<link rel="stylesheet" href="/css/main.css"/> <link rel="stylesheet" href="/css/main.css"/>
<script src="https://unpkg.com/htmx.org@1.9.11" integrity="sha384-0gxUXCCR8yv9FM2b+U3FDbsKthCI66oH5IA9fHppQq9DDMHuMauqq1ZHBpJxQ0J0" crossorigin="anonymous"></script> <script src="https://unpkg.com/htmx.org@1.9.11" integrity="sha384-0gxUXCCR8yv9FM2b+U3FDbsKthCI66oH5IA9fHppQq9DDMHuMauqq1ZHBpJxQ0J0" crossorigin="anonymous"></script>
<meta charset="utf-8"/> <meta charset="utf-8"/>

View File

@ -1,10 +1,29 @@
package sources package sources
import "git.jamestombleson.com/jtom38/newsbot-portal/internal/views/layout" import (
import "git.jamestombleson.com/jtom38/newsbot-portal/internal/models" "git.jamestombleson.com/jtom38/newsbot-portal/components/bulma"
bf "git.jamestombleson.com/jtom38/newsbot-portal/components/bulma/form"
bl "git.jamestombleson.com/jtom38/newsbot-portal/components/bulma/layout"
"git.jamestombleson.com/jtom38/newsbot-portal/internal/models"
"git.jamestombleson.com/jtom38/newsbot-portal/internal/views/layout"
)
var (
p = bf.NewParam{
HxPost: "/sources/add",
}
)
templ Add(model models.AddSourcePayloadModel) { templ Add(model models.AddSourcePayloadModel) {
@layout.WithTemplate() { @layout.WithTemplate() {
<form hx-post="/sources/add"></form> @bl.Hero("New Source", "")
@bulma.Block() {
At this time only direct RSS links are allowed to be provided.
}
@bf.New(p) {
@bf.TextInput("name", bf.InputTypeText, "Name of the site")
@bf.TextInput("url", bf.InputTypeText, "RSS URL")
@bf.Submit("Submit", bulma.ColorPrimary)
}
} }
} }

View File

@ -0,0 +1,11 @@
package sources
import "git.jamestombleson.com/jtom38/newsbot-portal/components/bulma"
templ AddAfter(message string, isError bool) {
if isError {
@bulma.Notification(message, bulma.ColorError)
} else {
@bulma.Notification("The requested source was added to the server", bulma.ColorSuccess)
}
}

View File

@ -1,12 +1,23 @@
package sources package sources
import "git.jamestombleson.com/jtom38/newsbot-portal/internal/views/layout" import (
import "git.jamestombleson.com/jtom38/newsbot-portal/internal/models" "git.jamestombleson.com/jtom38/newsbot-portal/components/bulma"
bh "git.jamestombleson.com/jtom38/newsbot-portal/components/bulma/html"
"git.jamestombleson.com/jtom38/newsbot-portal/internal/models"
"git.jamestombleson.com/jtom38/newsbot-portal/internal/views/layout"
)
templ ListAll(model models.ListAllSourcesViewModel) { templ ListAll(model models.ListAllSourcesViewModel) {
@layout.WithTemplate() { @layout.WithTemplate() {
@bulma.Button() {
@bh.ALink("/sources/add", "New Source")
}
@bh.Br()
@bh.Br()
for _, item := range model.Items { for _, item := range model.Items {
<a href={ templ.SafeURL(item.Url) } target="_blank" rel="noopener noreferrer">{ item.DisplayName } - { item.Source } </a> @bulma.ButtonColor(bulma.ColorPrimary) {
@bulma.ANewTab(item.Url, item.DisplayName)
}
<br/> <br/>
} }
} }

View File

@ -1,17 +1,16 @@
package users package users
import "git.jamestombleson.com/jtom38/newsbot-portal/internal/models" import (
"git.jamestombleson.com/jtom38/newsbot-portal/components/bulma"
"git.jamestombleson.com/jtom38/newsbot-portal/internal/models"
)
// This is returned after the user logs into the application. // This is returned after the user logs into the application.
// It just returns a partial view because it will overlap with the existing template. // It just returns a partial view because it will overlap with the existing template.
templ AfterLogin(vm models.AfterLoginViewModel) { templ AfterLogin(vm models.AfterLoginViewModel) {
if vm.Success { if vm.Success {
<div class="notification is-success"> @bulma.Notification(vm.Message, bulma.ColorSuccess)
{ vm.Message }
</div>
} else { } else {
<div class="notification is-error"> @bulma.Notification(vm.Message, bulma.ColorError)
{ vm.Message }
</div>
} }
} }

View File

@ -1,15 +1,13 @@
package users package users
import "git.jamestombleson.com/jtom38/newsbot-portal/components/bulma"
// This is returned after the user creates an account. // This is returned after the user creates an account.
// It just returns a partial view because it will overlap with the existing template. // It just returns a partial view because it will overlap with the existing template.
templ AfterSignUp(message string, success bool) { templ AfterSignUp(message string, success bool) {
if success { if success {
<div class="notification is-success"> @bulma.Notification(message, bulma.ColorSuccess)
{ message }
</div>
} else { } else {
<div class="notification is-error"> @bulma.Notification(message, bulma.ColorError)
{ message }
</div>
} }
} }

View File

@ -0,0 +1,32 @@
package users
import (
"git.jamestombleson.com/jtom38/newsbot-portal/components/bulma/form"
"git.jamestombleson.com/jtom38/newsbot-portal/internal/views/layout"
)
var (
p = form.NewParam{
HxPost: "/users/login",
}
)
templ LoginNew() {
@layout.WithTemplate() {
@form.New(p) {
@form.Field() {
@form.Label("Username")
@form.Control() {
@form.TextInput("username", "text", "email address")
}
}
@form.Field() {
@form.Label("Password")
@form.Control() {
@form.TextInput("password", form.InputTypePassword, "")
}
}
@form.Submit("Submit", "is-primary")
}
}
}

View File

@ -1,10 +1,10 @@
package users package users
import "git.jamestombleson.com/jtom38/newsbot-portal/internal/views/layout" import "git.jamestombleson.com/jtom38/newsbot-portal/internal/views/layout"
import "git.jamestombleson.com/jtom38/newsbot-portal/internal/views/bulma" import bl "git.jamestombleson.com/jtom38/newsbot-portal/components/bulma/layout"
templ Logout() { templ Logout() {
@layout.WithTemplate(){ @layout.WithTemplate(){
@bulma.Hero("You are out of here!", "Please login again when you are ready.") @bl.Hero("You are out of here!", "Please login again when you are ready.")
} }
} }

View File

@ -1,15 +1,20 @@
package users package users
import "git.jamestombleson.com/jtom38/newsbot-portal/internal/views/layout" import (
import "git.jamestombleson.com/jtom38/newsbot-portal/internal/views/bulma" "git.jamestombleson.com/jtom38/newsbot-portal/components/bulma"
bh "git.jamestombleson.com/jtom38/newsbot-portal/components/bulma/html"
bl "git.jamestombleson.com/jtom38/newsbot-portal/components/bulma/layout"
"git.jamestombleson.com/jtom38/newsbot-portal/internal/views/layout"
)
templ Profile() { templ Profile() {
@layout.WithTemplate() { @layout.WithTemplate() {
@bulma.Hero("Profile", "Here you can update your profile 😀") @bl.Hero("Profile", "Here you can update your profile 😀")
<button type="button" class="button"> @bulma.H2("Sessions", false)
<a href="/users/forcelogout">Logout Everywhere</a> @bulma.Button() {
Logout Everywhere</button> @bh.ALink("/users/forcelogout", "Terminate all active sessions")
<p class="subtitle">This will force all active sessions to stop working and require a new login.</p> }
@bulma.Subitle("This will force you to login again as the application will give you a new session value.")
} }
} }

View File

@ -1,23 +1,26 @@
package users package users
import "git.jamestombleson.com/jtom38/newsbot-portal/internal/views/layout" import (
"git.jamestombleson.com/jtom38/newsbot-portal/components/bulma/form"
"git.jamestombleson.com/jtom38/newsbot-portal/internal/views/layout"
)
templ SignUp() { templ SignUp() {
@layout.WithTemplate() { @layout.WithTemplate() {
<form hx-post="/users/signup"> @form.New(form.NewParam{HxPost: "/users/signup"}) {
<div class="field"> @form.Field(){
<label class="label">Username</label> @form.Label("Username")
<div class="control"> @form.Control() {
<input class="input" type="text" name="username" id="username"/> @form.TextInput("username", form.InputTypeText, "username or email address")
</div> }
</div> }
<div class="field"> @form.Field() {
<label class="label">Password</label> @form.Label("Password")
<div class="control"> @form.Control() {
<input class="input" type="password" name="password" id="exampleInputPassword1"/> @form.TextInput("password", form.InputTypePassword, "Nice strong password, like Ox!")
</div> }
</div> }
<button type="submit" class="button is-primary">Submit</button> @form.Submit("Submit", "")
</form> }
} }
} }