Complete transformation to htmx
This commit is contained in:
@@ -104,7 +104,7 @@ func (b *Backend) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == http.MethodOptions {
|
||||
w.Header().Set("Access-Control-Allow-Methods", "*")
|
||||
w.Header().Set("Access-Control-Allow-Credentials", "true")
|
||||
w.Header().Set("Access-Control-Allow-Headers", "Access-Control-Allow-Headers, Authorization, X-API-Key, Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers")
|
||||
w.Header().Set("Access-Control-Allow-Headers", "*")
|
||||
}
|
||||
}
|
||||
b.m.ServeHTTP(w, r)
|
||||
|
||||
@@ -3,6 +3,7 @@ package blog
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
@@ -13,6 +14,12 @@ import (
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
)
|
||||
|
||||
const authorInfo = `
|
||||
<table><tr>
|
||||
<td><img src="%v" alt="%v" class='author-pic'></td>
|
||||
<td><h3 class="author-title">%v</h3>%v</td>
|
||||
</tr></table>`
|
||||
|
||||
type Author struct {
|
||||
ID string `json:"id" bson:"_id"`
|
||||
Name string `json:"name" bson:"name"`
|
||||
@@ -20,6 +27,10 @@ type Author struct {
|
||||
PicURL string `json:"picurl" bson:"picurl"`
|
||||
}
|
||||
|
||||
func (a Author) HTML() string {
|
||||
return fmt.Sprintf(authorInfo, a.PicURL, a.Name+"'s profile picture", a.Name, a.About)
|
||||
}
|
||||
|
||||
func (b *BlogApp) AboutMe(ctx context.Context) (*Author, error) {
|
||||
res := b.authCol.FindOne(ctx, bson.M{"_id": "caleb_gardner"})
|
||||
if res.Err() != nil {
|
||||
|
||||
+37
-1
@@ -3,6 +3,7 @@ package blog
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
@@ -15,6 +16,13 @@ import (
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
const (
|
||||
blogTitle = "<h1 class='blog-title'><a hx-push-url='true' hx-get='/%v' hx-target='#content' href='/%v' style='text-decoration:none'>%v</a></h1>"
|
||||
blogAuthor = "<h4 class='blog-author'><i><b>By %v</b></i></h4>"
|
||||
blogCreate = "<h5 class='blog-time'><i>Written on: %v</i></h5>"
|
||||
blogMain = "<div class='blog'>%v</div>"
|
||||
)
|
||||
|
||||
type Blog struct {
|
||||
ID string `json:"id" bson:"_id"`
|
||||
Author string `json:"author" bson:"author"`
|
||||
@@ -27,6 +35,30 @@ type Blog struct {
|
||||
UpdateTime int64 `json:"updateTime" bson:"updateTime"`
|
||||
}
|
||||
|
||||
func (b *Blog) HTMX(blogApp *BlogApp, ctx context.Context) string {
|
||||
if b.StaticPage {
|
||||
return b.Blog
|
||||
}
|
||||
out := fmt.Sprintf(blogTitle, b.ID, b.ID, b.Title)
|
||||
auth, err := blogApp.GetAuthor(ctx, b)
|
||||
if err == nil {
|
||||
out += fmt.Sprintf(blogAuthor, auth.Name)
|
||||
} else {
|
||||
out += fmt.Sprintf(blogAuthor, "unknown")
|
||||
}
|
||||
cTime := time.Unix(b.CreateTime, 0).Format(time.DateOnly)
|
||||
if b.UpdateTime > b.CreateTime {
|
||||
out += fmt.Sprintf(blogCreate, cTime+"; Last updated on: "+time.Unix(b.UpdateTime, 0).Format(time.DateOnly))
|
||||
} else {
|
||||
out += fmt.Sprintf(blogCreate, cTime)
|
||||
}
|
||||
out += fmt.Sprintf(blogMain, b.Blog)
|
||||
if err == nil {
|
||||
out += "<h2 class='blog-author-info'>About the author:</h2>" + auth.HTML()
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (b *BlogApp) ConvertBlog(blog *Blog) {
|
||||
if !blog.StaticPage {
|
||||
blog.Blog = b.conv.HTMLConvert(blog.Blog)
|
||||
@@ -95,7 +127,11 @@ func (b *BlogApp) reqBlog(w http.ResponseWriter, r *http.Request) {
|
||||
backend.ReturnError(w, http.StatusInternalServerError, "internal", "Server error")
|
||||
return
|
||||
}
|
||||
json.NewEncoder(w).Encode(blog)
|
||||
if r.Header.Get("Hx-Request") == "true" {
|
||||
w.Write([]byte(blog.HTMX(b, r.Context())))
|
||||
} else {
|
||||
json.NewEncoder(w).Encode(blog)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BlogApp) createBlog(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
@@ -3,13 +3,24 @@ package blog
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/CalebQ42/darkstorm-server/internal/backend"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
const (
|
||||
portfolioTitle = "<h2 class='portfolio-title'>%v</h2>"
|
||||
portfolioLink = "<p class='portfolio-link'><a href='%v'>%v</a>"
|
||||
portfolioLanguage = "<p class='portfolio-language'><b>%v</b>: %v</p>"
|
||||
portfolioTech = "<p class='portfolio-tech'><b>Tech: </b>%v</p>"
|
||||
portfolioDesc = "<p class='portfolio-description'>%v</p>"
|
||||
)
|
||||
|
||||
type PortfolioProject struct {
|
||||
Title string `json:"_id" bson:"_id"`
|
||||
Order int `json:"order" bson:"order"`
|
||||
@@ -22,10 +33,80 @@ type PortfolioProject struct {
|
||||
} `json:"language" bson:"language"`
|
||||
}
|
||||
|
||||
func (b *BlogApp) Projects(ctx context.Context, languageFilter string) ([]PortfolioProject, error) {
|
||||
func (p PortfolioProject) HTMX() string {
|
||||
out := fmt.Sprintf(portfolioTitle, p.Title)
|
||||
out += fmt.Sprintf(portfolioLink, p.Repository, p.Repository)
|
||||
for _, l := range p.Languages {
|
||||
out += fmt.Sprintf(portfolioLanguage, l.Language, l.Dates)
|
||||
}
|
||||
out += fmt.Sprintf(portfolioTech, strings.Join(p.Technologies, ", "))
|
||||
out += fmt.Sprintf(portfolioDesc, p.Description)
|
||||
return out
|
||||
}
|
||||
|
||||
type Portfolio []PortfolioProject
|
||||
|
||||
const (
|
||||
portfolioSelector = `
|
||||
<p>Tech Filter:
|
||||
<select
|
||||
id='techSelect'
|
||||
name='tech'
|
||||
hx-get='http://localhost:2323/blog/portfolio'
|
||||
hx-target='#projects'>
|
||||
%v
|
||||
</select>
|
||||
</p>`
|
||||
portfolioSelectorOption = "<option value='%v'%v>%v</option>"
|
||||
)
|
||||
|
||||
func (p Portfolio) FullHTMX(ctx context.Context, blogApp *BlogApp, selectedTech string) string {
|
||||
aboutMe := "<h1 class='about-me-header'>About Me</h1>"
|
||||
if me, err := blogApp.AboutMe(ctx); err != nil {
|
||||
aboutMe += "Error getting info about me :("
|
||||
} else {
|
||||
aboutMe += me.HTML()
|
||||
}
|
||||
aboutMe += "<h1 class='my-projects-header' style='margin-bottom:15px'>My Projects</h1>"
|
||||
tech := make(map[string]struct{})
|
||||
for i := range p {
|
||||
for _, t := range p[i].Technologies {
|
||||
tech[t] = struct{}{}
|
||||
}
|
||||
}
|
||||
techKeys := make([]string, 0, len(tech))
|
||||
for k := range tech {
|
||||
techKeys = append(techKeys, k)
|
||||
}
|
||||
slices.Sort(techKeys)
|
||||
var out string
|
||||
if selectedTech == "" {
|
||||
out = fmt.Sprintf(portfolioSelectorOption, "", " selected=true", "All")
|
||||
} else {
|
||||
out = fmt.Sprintf(portfolioSelectorOption, "", "", "All")
|
||||
}
|
||||
for _, k := range techKeys {
|
||||
if selectedTech == strings.ToLower(k) {
|
||||
out += fmt.Sprintf(portfolioSelectorOption, k, " selected=true", k)
|
||||
} else {
|
||||
out += fmt.Sprintf(portfolioSelectorOption, k, "", k)
|
||||
}
|
||||
}
|
||||
return aboutMe + fmt.Sprintf(portfolioSelector, out) + "<div id='projects'>" + p.HTMX() + "</div>"
|
||||
}
|
||||
|
||||
func (p Portfolio) HTMX() string {
|
||||
out := ""
|
||||
for _, proj := range p {
|
||||
out += proj.HTMX()
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (b *BlogApp) Projects(ctx context.Context, techFilter string) (Portfolio, error) {
|
||||
filter := bson.M{}
|
||||
if languageFilter != "" {
|
||||
filter["language.language"] = languageFilter
|
||||
if techFilter != "" {
|
||||
filter = bson.M{"technologies": techFilter}
|
||||
}
|
||||
res, err := b.portfolioCol.Find(ctx, filter, options.Find().SetSort(bson.M{"order": 1}))
|
||||
if err != nil {
|
||||
@@ -37,10 +118,18 @@ func (b *BlogApp) Projects(ctx context.Context, languageFilter string) ([]Portfo
|
||||
}
|
||||
|
||||
func (b *BlogApp) reqPortfolio(w http.ResponseWriter, r *http.Request) {
|
||||
folio, err := b.Projects(r.Context(), r.URL.Query().Get("lang"))
|
||||
folio, err := b.Projects(r.Context(), r.URL.Query().Get("tech"))
|
||||
if err != nil {
|
||||
backend.ReturnError(w, http.StatusInternalServerError, "internal", "Server Error")
|
||||
return
|
||||
}
|
||||
json.NewEncoder(w).Encode(folio)
|
||||
if r.Header.Get("Hx-Request") == "true" {
|
||||
if r.URL.Query().Has("tech") {
|
||||
w.Write([]byte(folio.HTMX()))
|
||||
} else {
|
||||
w.Write([]byte(folio.FullHTMX(r.Context(), b, r.URL.Query().Get("tech"))))
|
||||
}
|
||||
} else {
|
||||
json.NewEncoder(w).Encode(folio)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user