so much stuff

This commit is contained in:
maddalax 2024-09-11 16:08:35 -05:00
parent c0137247b7
commit de28a6a783
13 changed files with 238 additions and 39 deletions

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="dataSourceStorageLocal" created-in="GO-242.21829.165">
<component name="dataSourceStorageLocal" created-in="GO-242.21829.220">
<data-source name="site.db" uuid="6b63d5bd-e451-4904-b659-21db5c54c16d">
<database-info product="SQLite" version="3.40.1" jdbc-version="4.2" driver-name="SQLite JDBC" driver-version="3.40.1.0" dbms="SQLITE" exact-version="3.40.1" exact-driver-version="3.40">
<identifier-quote-string>&quot;</identifier-quote-string>

2
go.mod
View file

@ -3,10 +3,12 @@ module mhtml
go 1.20
require (
github.com/dave/jennifer v1.7.1
github.com/fsnotify/fsnotify v1.7.0
github.com/gofiber/fiber/v2 v2.52.4
github.com/google/uuid v1.6.0
github.com/redis/go-redis/v9 v9.0.5
golang.org/x/net v0.21.0
golang.org/x/tools v0.4.0
)

4
go.sum
View file

@ -4,6 +4,8 @@ github.com/bsm/ginkgo/v2 v2.7.0 h1:ItPMPH90RbmZJt5GtkcNvIRuGEdwlBItdNVoyzaNQao=
github.com/bsm/gomega v1.26.0 h1:LhQm+AFcgV2M0WyKroMASzAzCAJVpAxQXv4SaI9a69Y=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/dave/jennifer v1.7.1 h1:B4jJJDHelWcDhlRQxWeo0Npa/pYKBLrirAQoTN45txo=
github.com/dave/jennifer v1.7.1/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3RmGZc=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
@ -31,6 +33,8 @@ github.com/valyala/fasthttp v1.52.0 h1:wqBQpxH71XW0e2g+Og4dzQM8pk34aFYlA1Ga8db7g
github.com/valyala/fasthttp v1.52.0/go.mod h1:hf5C4QnVMkNXMspnsUlfM3WitlgYflyhHYoKol/szxQ=
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=

View file

@ -38,6 +38,10 @@ func NewPageWithHttpMethod(httpMethod string, root *Node) *Page {
}
}
func WrapPartial(ctx *fiber.Ctx, cb func(ctx *fiber.Ctx) *Partial) *Node {
return cb(ctx).Root
}
func NewPartialWithHeaders(headers *Headers, root *Node) *Partial {
return &Partial{
Headers: headers,

View file

@ -25,7 +25,7 @@ func LiveReloadHandler(c *fiber.Ctx) error {
}
func LiveReload() *Node {
return Div(Get("/livereload"), Trigger("every 2s"))
return Div(Get("/livereload"), Trigger("every 200ms"))
}
func AddLiveReloadHandler(path string, app *fiber.App) {

22
main.go
View file

@ -4,11 +4,9 @@ import (
"github.com/gofiber/fiber/v2"
"github.com/google/uuid"
"log"
"mhtml/database"
"mhtml/h"
"mhtml/pages"
"mhtml/partials"
"mhtml/partials/sheet"
"time"
)
@ -42,7 +40,7 @@ func main() {
return err
})
f.Get("/mhtml/partials*", func(ctx *fiber.Ctx) error {
f.All("/mhtml/partials*", func(ctx *fiber.Ctx) error {
partial := partials.GetPartialFromContext(ctx)
if partial == nil {
return ctx.SendStatus(404)
@ -51,24 +49,6 @@ func main() {
})
pages.RegisterPages(f)
f.Post("/api/patients", func(ctx *fiber.Ctx) error {
name := ctx.FormValue("name")
reason := ctx.FormValue("reason-for-visit")
location := ctx.FormValue("location-name")
database.HSet("patients", uuid.New().String(), partials.Patient{
Name: name,
ReasonForVisit: reason,
AppointmentDate: time.Now(),
LocationName: location,
})
return h.PartialViewWithHeaders(ctx, &map[string]string{
"HX-Trigger": "patient-added",
}, sheet.Close(ctx))
})
h.Start(f, h.App{
LiveReload: true,
})

View file

@ -4,7 +4,7 @@ import (
"github.com/gofiber/fiber/v2"
"mhtml/h"
"mhtml/pages/base"
"mhtml/partials"
"mhtml/partials/patient"
)
func PatientsIndex(ctx *fiber.Ctx) *h.Page {
@ -15,9 +15,9 @@ func PatientsIndex(ctx *fiber.Ctx) *h.Page {
h.Div(
h.Class("flex justify-between items-center"),
h.P("Manage Patients", h.Class("text-lg font-bold")),
partials.AddPatientButton(),
patient.AddPatientButton(),
),
h.ViewWithTriggers(partials.PatientList, "load", "patient-added from:body"),
h.ViewWithTriggers(patient.List, "load", "patient-added from:body"),
),
),
))

View file

@ -3,6 +3,7 @@ package partials
import "mhtml/h"
import "github.com/gofiber/fiber/v2"
import "mhtml/partials/patient"
import "mhtml/partials/sheet"
func GetPartialFromContext(ctx *fiber.Ctx) *h.Partial {
@ -13,11 +14,14 @@ func GetPartialFromContext(ctx *fiber.Ctx) *h.Partial {
if path == "NewsSheetOpenCount" || path == "/mhtml/partials.NewsSheetOpenCount" {
return NewsSheetOpenCount(ctx)
}
if path == "PatientList" || path == "/mhtml/partials.PatientList" {
return PatientList(ctx)
if path == "Create" || path == "/mhtml/partials/patient.Create" {
return patient.Create(ctx)
}
if path == "AddPatientSheet" || path == "/mhtml/partials.AddPatientSheet" {
return AddPatientSheet(ctx)
if path == "List" || path == "/mhtml/partials/patient.List" {
return patient.List(ctx)
}
if path == "AddPatientSheet" || path == "/mhtml/partials/patient.AddPatientSheet" {
return patient.AddPatientSheet(ctx)
}
if path == "Close" || path == "/mhtml/partials/sheet.Close" {
return sheet.Close(ctx)

View file

@ -15,8 +15,10 @@ func NavBar() *h.Node {
{"Patients", "/patients"},
}
return h.Nav(h.Class("flex gap-4 items-center p-4 text-slate-600"), h.Children(h.Map(links, func(link Link) *h.Node {
return h.A(link.Name, h.Href(link.Path), h.Class("cursor-pointer hover:text-blue-400"))
}),
))
return h.Nav(h.Class("flex gap-4 items-center p-4 text-slate-600"),
h.Children(
h.Map(links, func(link Link) *h.Node {
return h.A(link.Name, h.Href(link.Path), h.Class("cursor-pointer hover:text-blue-400"))
}),
))
}

View file

@ -0,0 +1,29 @@
package patient
import (
"github.com/gofiber/fiber/v2"
"github.com/google/uuid"
"mhtml/database"
"mhtml/h"
"mhtml/partials/sheet"
"time"
)
func Create(ctx *fiber.Ctx) *h.Partial {
name := ctx.FormValue("name")
reason := ctx.FormValue("reason-for-visit")
location := ctx.FormValue("location-name")
database.HSet("patients", uuid.New().String(), Patient{
Name: name,
ReasonForVisit: reason,
AppointmentDate: time.Now(),
LocationName: location,
})
headers := &map[string]string{
"HX-Trigger": "patient-added",
}
return h.NewPartialWithHeaders(headers, h.WrapPartial(ctx, sheet.Close))
}

View file

@ -1,4 +1,4 @@
package partials
package patient
import (
"github.com/gofiber/fiber/v2"
@ -16,7 +16,7 @@ type Patient struct {
LocationName string
}
func PatientList(ctx *fiber.Ctx) *h.Partial {
func List(ctx *fiber.Ctx) *h.Partial {
patients, err := database.HList[Patient]("patients")
if err != nil {
@ -36,7 +36,7 @@ func PatientList(ctx *fiber.Ctx) *h.Partial {
return h.NewPartial(h.Div(
h.Class("mt-8"),
h.Id("patient-list"),
h.List(patients, PatientRow),
h.List(patients, Row),
))
}
@ -54,7 +54,7 @@ func AddPatientSheet(ctx *fiber.Ctx) *h.Partial {
func addPatientForm() *h.Node {
return h.Form(
h.Post("/api/patients"),
h.Post(h.GetPartialPath(Create)),
h.Class("flex flex-col gap-2"),
ui.Input(ui.InputProps{
Type: "text",
@ -84,7 +84,7 @@ func addPatientForm() *h.Node {
)
}
func PatientRow(patient *Patient, index int) *h.Node {
func Row(patient *Patient, index int) *h.Node {
return h.Div(
h.Class("flex flex-col gap-2 rounded p-4", h.Ternary(index%2 == 0, "bg-red-100", "")),
h.Pf("Name: %s", patient.Name),

55
sandbox.go Normal file
View file

@ -0,0 +1,55 @@
package main
import "mhtml/h"
func init() {
h.Div(
h.Id("sandbox"),
h.Button(
h.Id("btn"),
h.Class("bg-blue-500 text-white p-2"),
),
)
h.Html(
h.Head(),
h.Body(
h.Nav(
h.Class("flex gap-4 items-center p-4 text-slate-600 "),
h.A(h.Href("/"), h.Class("cursor-pointer hover:text-blue-400 ")),
h.A(h.Class("cursor-pointer hover:text-blue-400 "),
h.Href("/news")), h.A(
h.Href("/patients"),
h.Class("cursor-pointer hover:text-blue-400 "))),
h.Div(
h.Id("active-modal")),
h.Div(h.Class("flex flex-col gap-2 bg-white h-full "),
h.Div(h.Class("flex flex-col p-4 w-full "),
h.Div(h.Div(h.Class("flex justify-between items-center "),
h.P(
h.Class("text-lg font-bold ")),
h.Button(h.HxTarget("#active-modal"),
h.Type("button"),
h.Id("add-patient"),
h.Class("flex gap-1 items-center border p-4 rounded cursor-hover bg-blue-700 text-white rounded p-2 h-12 "),
h.HxGet("mhtml/partials/patient.AddPatientSheet"))),
h.Div(h.HxGet("mhtml/partials/patient.List"),
h.HxTrigger("load, patient-added from:body"),
h.Class(""), h.Div(h.Class("mt-8"),
h.Id("patient-list"),
h.Div(h.Class("flex flex-col gap-2 rounded p-4 bg-red-100 "),
h.P(),
h.P()),
),
),
),
),
),
h.Div(
h.HxGet("/livereload"),
h.HxTrigger("every 200ms"),
h.Class(""),
),
))
}

119
tooling/htmltogo/entry.go Normal file
View file

@ -0,0 +1,119 @@
package main
import (
"bytes"
"fmt"
"log"
"strings"
"github.com/dave/jennifer/jen"
"golang.org/x/net/html"
)
func main() {
// Example HTML input
htmlData := `
<body><nav class="flex gap-4 items-center p-4 text-slate-600 "><a href="/" class="cursor-pointer hover:text-blue-400 ">Home</a><a class="cursor-pointer hover:text-blue-400 " href="/news">News</a><a href="/patients" class="cursor-pointer hover:text-blue-400 ">Patients</a></nav><div id="active-modal"></div><div class="flex flex-col gap-2 bg-white h-full "><div class="flex flex-col p-4 w-full "><div><div class="flex justify-between items-center "><p class="text-lg font-bold ">Manage Patients</p><button hx-target="#active-modal" type="button" id="add-patient" class="flex gap-1 items-center border p-4 rounded cursor-hover bg-blue-700 text-white rounded p-2 h-12 " hx-get="mhtml/partials/patient.AddPatientSheet">Add Patient</button></div><div hx-get="mhtml/partials/patient.List" hx-trigger="load, patient-added from:body" class=""><div class="mt-8" id="patient-list"><div class="flex flex-col gap-2 rounded p-4 bg-red-100 "><p>Name: Sydne</p><p>Reason for visit: arm hurts</p></div></div></div></div></div></div><div hx-get="/livereload" hx-trigger="every 200ms" class=""></div></body>
`
// Parse the HTML
doc, err := html.Parse(bytes.NewReader([]byte(htmlData)))
if err != nil {
log.Fatal(err)
}
// Create a new Jennifer file
f := jen.NewFile("main")
// Generate Jennifer code for the parsed HTML tree
generatedCode := processNode(doc.FirstChild)
// Add the generated code to the file
f.Func().Id("Render").Params().Block(generatedCode...)
// Render the generated code
var buf bytes.Buffer
err = f.Render(&buf)
if err != nil {
log.Fatal(err)
}
//// Format the generated code
//formattedCode, err := format.Source(buf.Bytes())
//if err != nil {
// log.Fatal(err)
//}
// Output the formatted code
fmt.Println(string(buf.Bytes()))
}
// Recursively process the HTML nodes and generate Jennifer code
func processNode(n *html.Node) []jen.Code {
var code []jen.Code
// Only process element nodes
if n.Type == html.ElementNode {
// Create a dynamic method call based on the tag name
tagMethod := strings.Title(n.Data) // Capitalize the first letter of the tag
// Add dynamic method call for the tag (e.g., h.Div(), h.Button(), etc.)
code = append(code, jen.Id("h").Dot(tagMethod).Call(mergeArgs(n)...))
}
return code
}
// Merge attributes and children into a single slice for Call()
func mergeArgs(n *html.Node) []jen.Code {
// Process attributes
attrs := processAttributes(n.Attr)
// Process children
children := processChildren(n)
// Combine attributes and children into one slice
return append(attrs, children...)
}
// Process child nodes of a given HTML node
func processChildren(n *html.Node) []jen.Code {
var children []jen.Code
for c := n.FirstChild; c != nil; c = c.NextSibling {
children = append(children, processNode(c)...)
}
return children
}
func FormatFieldName(name string) string {
split := strings.Split(name, "_")
if strings.Contains(name, "-") {
split = strings.Split(name, "-")
}
parts := make([]string, 0)
for _, s := range split {
parts = append(parts, PascalCase(s))
}
return strings.Join(parts, "")
}
func PascalCase(s string) string {
if s == "" {
return s
}
// Convert the first rune (character) to uppercase and concatenate with the rest of the string
return strings.ToUpper(string(s[0])) + s[1:]
}
// Process the attributes of an HTML node and return Jennifer code
func processAttributes(attrs []html.Attribute) []jen.Code {
var args []jen.Code
for _, attr := range attrs {
// Dynamically handle all attributes
attrMethod := FormatFieldName(attr.Key) // E.g., convert "data-role" to "DataRole"
args = append(args, jen.Id("h").Dot(attrMethod).Call(jen.Lit(attr.Val)))
}
return args
}