mirror of
https://github.com/eduardolat/pgbackweb.git
synced 2026-01-26 14:39:24 -06:00
Merge pull request #81 from eduardolat/refactor-nodx
Refactor components to use NodX library, replacing gomponents referen…
This commit is contained in:
4
go.mod
4
go.mod
@@ -5,14 +5,14 @@ go 1.23.1
|
||||
require (
|
||||
github.com/adhocore/gronx v1.8.1
|
||||
github.com/aws/aws-sdk-go v1.54.20
|
||||
github.com/eduardolat/gomponents-lucide v1.2.0
|
||||
github.com/go-co-op/gocron/v2 v2.11.0
|
||||
github.com/go-playground/validator/v10 v10.22.0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/labstack/echo/v4 v4.12.0
|
||||
github.com/lib/pq v1.10.9
|
||||
github.com/maragudk/gomponents v0.20.4
|
||||
github.com/nodxdev/nodxgo v0.2.1
|
||||
github.com/nodxdev/nodxgo-lucide v0.1.0
|
||||
github.com/orsinium-labs/enum v1.4.0
|
||||
github.com/stretchr/testify v1.9.0
|
||||
golang.org/x/crypto v0.25.0
|
||||
|
||||
8
go.sum
8
go.sum
@@ -6,8 +6,6 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/eduardolat/gomponents-lucide v1.2.0 h1:IzvsHfdfMjDF0c8aOxG7P0lawaSjwVLBo7z6VASOBbc=
|
||||
github.com/eduardolat/gomponents-lucide v1.2.0/go.mod h1:C0Yx64QM2RXIal8pl/HdWU0xac0uek+IQI2eZAaeAwk=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||
github.com/go-co-op/gocron/v2 v2.11.0 h1:IOowNA6SzwdRFnD4/Ol3Kj6G2xKfsoiiGq2Jhhm9bvE=
|
||||
@@ -42,8 +40,6 @@ github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/maragudk/gomponents v0.20.4 h1:8ayYSzCyz1EUl/+LU5vOBR/K2wI18W7SscNAzzsxsDo=
|
||||
github.com/maragudk/gomponents v0.20.4/go.mod h1:nHkNnZL6ODgMBeJhrZjkMHVvNdoYsfmpKB2/hjdQ0Hg=
|
||||
github.com/matryer/is v1.4.1 h1:55ehd8zaGABKLXQUe2awZ99BD/PTc2ls+KV/dXphgEQ=
|
||||
github.com/matryer/is v1.4.1/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
@@ -51,6 +47,10 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/nodxdev/nodxgo v0.2.1 h1:itU0ynZnPOgTtMpf7wDa4XR00cLMT9YUUK1QMg+zaUI=
|
||||
github.com/nodxdev/nodxgo v0.2.1/go.mod h1:6RhpuOptMO8HT7ZGIzyAF+iH8ozJfRaneJZ1ehBp2YQ=
|
||||
github.com/nodxdev/nodxgo-lucide v0.1.0 h1:C+ftEo944pzC2lHcwuzr1o1PghZVxy3s0CF+6WEM8xE=
|
||||
github.com/nodxdev/nodxgo-lucide v0.1.0/go.mod h1:nw7hR6bEb9FtXzd+vnk0wkJZTl/YfM87faSx5pMWDBY=
|
||||
github.com/orsinium-labs/enum v1.4.0 h1:3NInlfV76kuAg0kq2FFUondmg3WO7gMEgrPPrlzLDUM=
|
||||
github.com/orsinium-labs/enum v1.4.0/go.mod h1:Qj5IK2pnElZtkZbGDxZMjpt7SUsn4tqE5vRelmWaBbc=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
|
||||
@@ -12,9 +12,9 @@ type renderer interface {
|
||||
Render(w io.Writer) error
|
||||
}
|
||||
|
||||
// RenderGomponent renders a gomponents component to the response of
|
||||
// RenderNodx renders a NodX component to the response of
|
||||
// the echo context.
|
||||
func RenderGomponent(c echo.Context, code int, component renderer) error {
|
||||
func RenderNodx(c echo.Context, code int, component renderer) error {
|
||||
if component == nil {
|
||||
return c.NoContent(code)
|
||||
}
|
||||
@@ -24,7 +24,7 @@ func (m *nokRenderer) Render(w io.Writer) error {
|
||||
return errors.New("render nok")
|
||||
}
|
||||
|
||||
func TestRenderGomponent(t *testing.T) {
|
||||
func TestRenderNodx(t *testing.T) {
|
||||
// Setup renderer mocks
|
||||
okRendererIns := new(okRenderer)
|
||||
nokRendererIns := new(nokRenderer)
|
||||
@@ -35,7 +35,7 @@ func TestRenderGomponent(t *testing.T) {
|
||||
rec := httptest.NewRecorder()
|
||||
c := e.NewContext(req, rec)
|
||||
|
||||
err := RenderGomponent(c, http.StatusOK, okRendererIns)
|
||||
err := RenderNodx(c, http.StatusOK, okRendererIns)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, http.StatusOK, c.Response().Status)
|
||||
assert.Equal(t, "render ok", rec.Body.String())
|
||||
@@ -47,20 +47,20 @@ func TestRenderGomponent(t *testing.T) {
|
||||
rec := httptest.NewRecorder()
|
||||
c := e.NewContext(req, rec)
|
||||
|
||||
err := RenderGomponent(c, http.StatusOK, nokRendererIns)
|
||||
err := RenderNodx(c, http.StatusOK, nokRendererIns)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, http.StatusInternalServerError, rec.Code)
|
||||
assert.Equal(t, "render nok", rec.Body.String())
|
||||
})
|
||||
}
|
||||
|
||||
func TestRenderNilGomponent(t *testing.T) {
|
||||
func TestRenderNilNodx(t *testing.T) {
|
||||
e := echo.New()
|
||||
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||
rec := httptest.NewRecorder()
|
||||
c := e.NewContext(req, rec)
|
||||
|
||||
err := RenderGomponent(c, http.StatusOK, nil)
|
||||
err := RenderNodx(c, http.StatusOK, nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, http.StatusOK, rec.Code)
|
||||
assert.Equal(t, "", rec.Body.String())
|
||||
@@ -1,3 +0,0 @@
|
||||
svg[data-glucide="icon"]:not([class*="size-"]) {
|
||||
@apply size-4;
|
||||
}
|
||||
3
internal/view/static/css/partials/nodx-lucide.css
Normal file
3
internal/view/static/css/partials/nodx-lucide.css
Normal file
@@ -0,0 +1,3 @@
|
||||
svg[data-nodxgo="lucide"]:not([class*="size-"]) {
|
||||
@apply size-4;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
@import "./partials/tailwind.css";
|
||||
@import "./partials/alpine.css";
|
||||
@import "./partials/general.css";
|
||||
@import "./partials/glucide.css";
|
||||
@import "./partials/nodx-lucide.css";
|
||||
@import "./partials/htmx.css";
|
||||
@import "./partials/slim-select.css";
|
||||
@import "./partials/notyf.css";
|
||||
|
||||
@@ -1,120 +1,122 @@
|
||||
package alpine
|
||||
|
||||
import "github.com/maragudk/gomponents"
|
||||
import (
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
)
|
||||
|
||||
// Template is a generic function for rendering an <template> element.
|
||||
func Template(children ...gomponents.Node) gomponents.Node {
|
||||
return gomponents.El("template", children...)
|
||||
func Template(children ...nodx.Node) nodx.Node {
|
||||
return nodx.El("template", children...)
|
||||
}
|
||||
|
||||
// XData is an attribute that renders a x-data="value]" attribute.
|
||||
//
|
||||
// https://alpinejs.dev/directives/data
|
||||
func XData(value string) gomponents.Node {
|
||||
return gomponents.Attr("x-data", value)
|
||||
func XData(value string) nodx.Node {
|
||||
return nodx.Attr("x-data", value)
|
||||
}
|
||||
|
||||
// XFor is an attribute that renders a x-for="value]" attribute.
|
||||
//
|
||||
// https://alpinejs.dev/directives/for
|
||||
func XFor(value string) gomponents.Node {
|
||||
return gomponents.Attr("x-for", value)
|
||||
func XFor(value string) nodx.Node {
|
||||
return nodx.Attr("x-for", value)
|
||||
}
|
||||
|
||||
// XInit is an attribute that renders a x-init="[value]" attribute.
|
||||
//
|
||||
// https://alpinejs.dev/directives/init
|
||||
func XInit(value string) gomponents.Node {
|
||||
return gomponents.Attr("x-init", value)
|
||||
func XInit(value string) nodx.Node {
|
||||
return nodx.Attr("x-init", value)
|
||||
}
|
||||
|
||||
// XShow is an attribute that renders a x-show="[vlue]" attribute.
|
||||
//
|
||||
// https://alpinejs.dev/directives/show
|
||||
func XShow(value string) gomponents.Node {
|
||||
return gomponents.Attr("x-show", value)
|
||||
func XShow(value string) nodx.Node {
|
||||
return nodx.Attr("x-show", value)
|
||||
}
|
||||
|
||||
// XBind is an attribute that renders a x-bind:[targetAttr]="[value]" attribute.
|
||||
//
|
||||
// https://alpinejs.dev/directives/bind
|
||||
func XBind(targetAttr string, value string) gomponents.Node {
|
||||
return gomponents.Attr("x-bind:"+targetAttr, value)
|
||||
func XBind(targetAttr string, value string) nodx.Node {
|
||||
return nodx.Attr("x-bind:"+targetAttr, value)
|
||||
}
|
||||
|
||||
// XOn is an attribute that renders a x-on:[targetEvent]="[value]" attribute.
|
||||
//
|
||||
// https://alpinejs.dev/directives/on
|
||||
func XOn(targetEvent string, value string) gomponents.Node {
|
||||
return gomponents.Attr("x-on:"+targetEvent, value)
|
||||
func XOn(targetEvent string, value string) nodx.Node {
|
||||
return nodx.Attr("x-on:"+targetEvent, value)
|
||||
}
|
||||
|
||||
// XText is an attribute that renders a x-text="[value]" attribute.
|
||||
//
|
||||
// https://alpinejs.dev/directives/text
|
||||
func XText(value string) gomponents.Node {
|
||||
return gomponents.Attr("x-text", value)
|
||||
func XText(value string) nodx.Node {
|
||||
return nodx.Attr("x-text", value)
|
||||
}
|
||||
|
||||
// XHTML is an attribute that renders a x-html="[value]" attribute.
|
||||
//
|
||||
// https://alpinejs.dev/directives/html
|
||||
func XHTML(value string) gomponents.Node {
|
||||
return gomponents.Attr("x-html", value)
|
||||
func XHTML(value string) nodx.Node {
|
||||
return nodx.Attr("x-html", value)
|
||||
}
|
||||
|
||||
// XModel is an attribute that renders a x-model="[value]" attribute.
|
||||
//
|
||||
// https://alpinejs.dev/directives/model
|
||||
func XModel(value string) gomponents.Node {
|
||||
return gomponents.Attr("x-model", value)
|
||||
func XModel(value string) nodx.Node {
|
||||
return nodx.Attr("x-model", value)
|
||||
}
|
||||
|
||||
// XTransition is an attribute that renders a x-transition attribute.
|
||||
//
|
||||
// https://alpinejs.dev/directives/transition
|
||||
func XTransition() gomponents.Node {
|
||||
return gomponents.Attr("x-transition")
|
||||
func XTransition() nodx.Node {
|
||||
return nodx.Attr("x-transition", "")
|
||||
}
|
||||
|
||||
// XTransitionFade is an attribute that renders a x-transition.opacity attribute.
|
||||
//
|
||||
// https://alpinejs.dev/directives/transition
|
||||
func XTransitionFade() gomponents.Node {
|
||||
return gomponents.Attr("x-transition.opacity")
|
||||
func XTransitionFade() nodx.Node {
|
||||
return nodx.Attr("x-transition.opacity", "")
|
||||
}
|
||||
|
||||
// XIgnore is an attribute that renders a x-ignore attribute.
|
||||
//
|
||||
// https://alpinejs.dev/directives/ignore
|
||||
func XIgnore() gomponents.Node {
|
||||
return gomponents.Attr("x-ignore")
|
||||
func XIgnore() nodx.Node {
|
||||
return nodx.Attr("x-ignore", "")
|
||||
}
|
||||
|
||||
// XRef is an attribute that renders a x-ref="[value]" attribute.
|
||||
//
|
||||
// https://alpinejs.dev/directives/ref
|
||||
func XRef(value string) gomponents.Node {
|
||||
return gomponents.Attr("x-ref", value)
|
||||
func XRef(value string) nodx.Node {
|
||||
return nodx.Attr("x-ref", value)
|
||||
}
|
||||
|
||||
// XCloak is an attribute that renders a x-cloak attribute.
|
||||
//
|
||||
// https://alpinejs.dev/directives/cloak
|
||||
func XCloak() gomponents.Node {
|
||||
return gomponents.Attr("x-cloak")
|
||||
func XCloak() nodx.Node {
|
||||
return nodx.Attr("x-cloak", "")
|
||||
}
|
||||
|
||||
// XTeleport is an attribute that renders a x-teleport="[value]" attribute.
|
||||
//
|
||||
// https://alpinejs.dev/directives/teleport
|
||||
func XTeleport(value string) gomponents.Node {
|
||||
return gomponents.Attr("x-teleport", value)
|
||||
func XTeleport(value string) nodx.Node {
|
||||
return nodx.Attr("x-teleport", value)
|
||||
}
|
||||
|
||||
// IF is an attribute that renders a x-if="[value]" attribute.
|
||||
//
|
||||
// https://alpinejs.dev/directives/if
|
||||
func XIf(value string) gomponents.Node {
|
||||
return gomponents.Attr("x-if", value)
|
||||
func XIf(value string) nodx.Node {
|
||||
return nodx.Attr("x-if", value)
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package auth
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
lucide "github.com/eduardolat/gomponents-lucide"
|
||||
"github.com/eduardolat/pgbackweb/internal/database/dbgen"
|
||||
"github.com/eduardolat/pgbackweb/internal/logger"
|
||||
"github.com/eduardolat/pgbackweb/internal/util/echoutil"
|
||||
@@ -12,8 +11,8 @@ import (
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/htmx"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/layout"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
lucide "github.com/nodxdev/nodxgo-lucide"
|
||||
)
|
||||
|
||||
func (h *handlers) createFirstUserPageHandler(c echo.Context) error {
|
||||
@@ -32,19 +31,19 @@ func (h *handlers) createFirstUserPageHandler(c echo.Context) error {
|
||||
return c.Redirect(http.StatusFound, "/auth/login")
|
||||
}
|
||||
|
||||
return echoutil.RenderGomponent(c, http.StatusOK, createFirstUserPage())
|
||||
return echoutil.RenderNodx(c, http.StatusOK, createFirstUserPage())
|
||||
}
|
||||
|
||||
func createFirstUserPage() gomponents.Node {
|
||||
content := []gomponents.Node{
|
||||
func createFirstUserPage() nodx.Node {
|
||||
content := []nodx.Node{
|
||||
component.H1Text("Create first user"),
|
||||
|
||||
html.Form(
|
||||
nodx.FormEl(
|
||||
htmx.HxPost("/auth/create-first-user"),
|
||||
htmx.HxDisabledELT("find button"),
|
||||
html.Class("mt-4 space-y-2"),
|
||||
nodx.Class("mt-4 space-y-2"),
|
||||
|
||||
html.Div(
|
||||
nodx.Div(
|
||||
component.InputControl(component.InputControlParams{
|
||||
Name: "name",
|
||||
Label: "Full name",
|
||||
@@ -70,9 +69,9 @@ func createFirstUserPage() gomponents.Node {
|
||||
Required: true,
|
||||
Type: component.InputTypePassword,
|
||||
AutoComplete: "new-password",
|
||||
Children: []gomponents.Node{
|
||||
html.MinLength("6"),
|
||||
html.MaxLength("50"),
|
||||
Children: []nodx.Node{
|
||||
nodx.Minlength("6"),
|
||||
nodx.Maxlength("50"),
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -82,19 +81,19 @@ func createFirstUserPage() gomponents.Node {
|
||||
Placeholder: "******",
|
||||
Required: true,
|
||||
Type: component.InputTypePassword,
|
||||
Children: []gomponents.Node{
|
||||
html.MinLength("6"),
|
||||
html.MaxLength("50"),
|
||||
Children: []nodx.Node{
|
||||
nodx.Minlength("6"),
|
||||
nodx.Maxlength("50"),
|
||||
},
|
||||
}),
|
||||
|
||||
html.Div(
|
||||
html.Class("pt-2 flex justify-end items-center space-x-2"),
|
||||
nodx.Div(
|
||||
nodx.Class("pt-2 flex justify-end items-center space-x-2"),
|
||||
component.HxLoadingMd(),
|
||||
html.Button(
|
||||
html.ID("create-first-user-button"),
|
||||
html.Class("btn btn-primary"),
|
||||
html.Type("submit"),
|
||||
nodx.Button(
|
||||
nodx.Id("create-first-user-button"),
|
||||
nodx.Class("btn btn-primary"),
|
||||
nodx.Type("submit"),
|
||||
component.SpanText("Create user and continue"),
|
||||
lucide.UserPlus(),
|
||||
),
|
||||
|
||||
@@ -3,7 +3,6 @@ package auth
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
lucide "github.com/eduardolat/gomponents-lucide"
|
||||
"github.com/eduardolat/pgbackweb/internal/logger"
|
||||
"github.com/eduardolat/pgbackweb/internal/util/echoutil"
|
||||
"github.com/eduardolat/pgbackweb/internal/validate"
|
||||
@@ -11,8 +10,8 @@ import (
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/htmx"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/layout"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
lucide "github.com/nodxdev/nodxgo-lucide"
|
||||
)
|
||||
|
||||
func (h *handlers) loginPageHandler(c echo.Context) error {
|
||||
@@ -31,17 +30,17 @@ func (h *handlers) loginPageHandler(c echo.Context) error {
|
||||
return c.Redirect(http.StatusFound, "/auth/create-first-user")
|
||||
}
|
||||
|
||||
return echoutil.RenderGomponent(c, http.StatusOK, loginPage())
|
||||
return echoutil.RenderNodx(c, http.StatusOK, loginPage())
|
||||
}
|
||||
|
||||
func loginPage() gomponents.Node {
|
||||
content := []gomponents.Node{
|
||||
func loginPage() nodx.Node {
|
||||
content := []nodx.Node{
|
||||
component.H1Text("Login"),
|
||||
|
||||
html.Form(
|
||||
nodx.FormEl(
|
||||
htmx.HxPost("/auth/login"),
|
||||
htmx.HxDisabledELT("find button"),
|
||||
html.Class("mt-4 space-y-2"),
|
||||
nodx.Class("mt-4 space-y-2"),
|
||||
|
||||
component.InputControl(component.InputControlParams{
|
||||
Name: "email",
|
||||
@@ -61,12 +60,12 @@ func loginPage() gomponents.Node {
|
||||
AutoComplete: "current-password",
|
||||
}),
|
||||
|
||||
html.Div(
|
||||
html.Class("pt-2 flex justify-end items-center space-x-2"),
|
||||
nodx.Div(
|
||||
nodx.Class("pt-2 flex justify-end items-center space-x-2"),
|
||||
component.HxLoadingMd(),
|
||||
html.Button(
|
||||
html.Class("btn btn-primary"),
|
||||
html.Type("submit"),
|
||||
nodx.Button(
|
||||
nodx.Class("btn btn-primary"),
|
||||
nodx.Type("submit"),
|
||||
component.SpanText("Login"),
|
||||
lucide.LogIn(),
|
||||
),
|
||||
|
||||
@@ -1,30 +1,28 @@
|
||||
package component
|
||||
|
||||
import (
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/components"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
)
|
||||
|
||||
type CardBoxParams struct {
|
||||
Class string
|
||||
Children []gomponents.Node
|
||||
Children []nodx.Node
|
||||
}
|
||||
|
||||
// CardBox renders a card box with the given children.
|
||||
func CardBox(params CardBoxParams) gomponents.Node {
|
||||
return html.Div(
|
||||
components.Classes{
|
||||
func CardBox(params CardBoxParams) nodx.Node {
|
||||
return nodx.Div(
|
||||
nodx.ClassMap{
|
||||
"rounded-box shadow-md bg-base-100 p-4": true,
|
||||
params.Class: true,
|
||||
},
|
||||
gomponents.Group(params.Children),
|
||||
nodx.Group(params.Children...),
|
||||
)
|
||||
}
|
||||
|
||||
// CardBoxSimple is the same as CardBox, but with a less verbose
|
||||
// api and default props. It renders a card box with the given children.
|
||||
func CardBoxSimple(children ...gomponents.Node) gomponents.Node {
|
||||
func CardBoxSimple(children ...nodx.Node) nodx.Node {
|
||||
return CardBox(CardBoxParams{
|
||||
Children: children,
|
||||
})
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
package component
|
||||
|
||||
import (
|
||||
lucide "github.com/eduardolat/gomponents-lucide"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/alpine"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/components"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
lucide "github.com/nodxdev/nodxgo-lucide"
|
||||
)
|
||||
|
||||
type ChangeThemeButtonParams struct {
|
||||
@@ -14,12 +12,12 @@ type ChangeThemeButtonParams struct {
|
||||
Size size
|
||||
}
|
||||
|
||||
func ChangeThemeButton(params ChangeThemeButtonParams) gomponents.Node {
|
||||
return html.Div(
|
||||
func ChangeThemeButton(params ChangeThemeButtonParams) nodx.Node {
|
||||
return nodx.Div(
|
||||
alpine.XData("alpineChangeThemeButton()"),
|
||||
alpine.XCloak(),
|
||||
|
||||
components.Classes{
|
||||
nodx.ClassMap{
|
||||
"dropdown": true,
|
||||
"dropdown-end": params.AlignsToEnd,
|
||||
"dropdown-right": params.Position == DropdownPositionRight,
|
||||
@@ -27,17 +25,17 @@ func ChangeThemeButton(params ChangeThemeButtonParams) gomponents.Node {
|
||||
"dropdown-top": params.Position == DropdownPositionTop,
|
||||
"dropdown-bottom": params.Position == DropdownPositionBottom,
|
||||
},
|
||||
html.Div(
|
||||
html.TabIndex("0"),
|
||||
html.Role("button"),
|
||||
components.Classes{
|
||||
nodx.Div(
|
||||
nodx.Tabindex("0"),
|
||||
nodx.Role("button"),
|
||||
nodx.ClassMap{
|
||||
"btn btn-neutral space-x-1": true,
|
||||
"btn-sm": params.Size == SizeSm,
|
||||
"btn-lg": params.Size == SizeLg,
|
||||
},
|
||||
|
||||
html.Div(
|
||||
html.Class("inline-block size-4"),
|
||||
nodx.Div(
|
||||
nodx.Class("inline-block size-4"),
|
||||
lucide.Laptop(alpine.XShow(`theme === "system"`)),
|
||||
lucide.Sun(alpine.XShow(`theme === "light"`)),
|
||||
lucide.Moon(alpine.XShow(`theme === "dark"`)),
|
||||
@@ -46,38 +44,38 @@ func ChangeThemeButton(params ChangeThemeButtonParams) gomponents.Node {
|
||||
SpanText("Theme"),
|
||||
lucide.ChevronDown(),
|
||||
),
|
||||
html.Ul(
|
||||
html.TabIndex("0"),
|
||||
components.Classes{
|
||||
nodx.Ul(
|
||||
nodx.Tabindex("0"),
|
||||
nodx.ClassMap{
|
||||
"dropdown-content": true,
|
||||
"bg-base-100": true,
|
||||
"rounded-btn shadow-md": true,
|
||||
"z-[1] w-[150px] p-2 space-y-2 my-2": true,
|
||||
},
|
||||
html.Li(
|
||||
html.Button(
|
||||
nodx.Li(
|
||||
nodx.Button(
|
||||
alpine.XOn("click", "setTheme('')"),
|
||||
html.Class("btn btn-neutral btn-block"),
|
||||
html.Type("button"),
|
||||
lucide.Laptop(html.Class("mr-1")),
|
||||
nodx.Class("btn btn-neutral btn-block"),
|
||||
nodx.Type("button"),
|
||||
lucide.Laptop(nodx.Class("mr-1")),
|
||||
SpanText("System"),
|
||||
),
|
||||
),
|
||||
html.Li(
|
||||
html.Button(
|
||||
nodx.Li(
|
||||
nodx.Button(
|
||||
alpine.XOn("click", "setTheme('light')"),
|
||||
html.Class("btn btn-neutral btn-block"),
|
||||
html.Type("button"),
|
||||
lucide.Sun(html.Class("mr-1")),
|
||||
nodx.Class("btn btn-neutral btn-block"),
|
||||
nodx.Type("button"),
|
||||
lucide.Sun(nodx.Class("mr-1")),
|
||||
SpanText("Light"),
|
||||
),
|
||||
),
|
||||
html.Li(
|
||||
html.Button(
|
||||
nodx.Li(
|
||||
nodx.Button(
|
||||
alpine.XOn("click", "setTheme('dark')"),
|
||||
html.Class("btn btn-neutral btn-block"),
|
||||
html.Type("button"),
|
||||
lucide.Moon(html.Class("mr-1")),
|
||||
nodx.Class("btn btn-neutral btn-block"),
|
||||
nodx.Type("button"),
|
||||
lucide.Moon(nodx.Class("mr-1")),
|
||||
SpanText("Dark"),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -4,15 +4,13 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
lucide "github.com/eduardolat/gomponents-lucide"
|
||||
"github.com/google/uuid"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/components"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
lucide "github.com/nodxdev/nodxgo-lucide"
|
||||
)
|
||||
|
||||
// CopyButtonSm is a small copy button.
|
||||
func CopyButtonSm(textToCopy string) gomponents.Node {
|
||||
func CopyButtonSm(textToCopy string) nodx.Node {
|
||||
return copyButton(copyButtonProps{
|
||||
TextToCopy: textToCopy,
|
||||
Size: SizeSm,
|
||||
@@ -20,14 +18,14 @@ func CopyButtonSm(textToCopy string) gomponents.Node {
|
||||
}
|
||||
|
||||
// CopyButtonMd is a medium copy button.
|
||||
func CopyButtonMd(textToCopy string) gomponents.Node {
|
||||
func CopyButtonMd(textToCopy string) nodx.Node {
|
||||
return copyButton(copyButtonProps{
|
||||
TextToCopy: textToCopy,
|
||||
})
|
||||
}
|
||||
|
||||
// CopyButtonLg is a large copy button.
|
||||
func CopyButtonLg(textToCopy string) gomponents.Node {
|
||||
func CopyButtonLg(textToCopy string) nodx.Node {
|
||||
return copyButton(copyButtonProps{
|
||||
TextToCopy: textToCopy,
|
||||
Size: SizeLg,
|
||||
@@ -44,24 +42,24 @@ type copyButtonProps struct {
|
||||
}
|
||||
|
||||
// copyButton is a button that copies text to the clipboard when clicked.
|
||||
func copyButton(props copyButtonProps) gomponents.Node {
|
||||
func copyButton(props copyButtonProps) nodx.Node {
|
||||
id := uuid.NewString()
|
||||
id = strings.ReplaceAll(id, "-", "")
|
||||
|
||||
sc := copyButtonScript(id, props.TextToCopy)
|
||||
|
||||
return html.Div(
|
||||
html.Class("inline-block tooltip tooltip-right"),
|
||||
html.Data("tip", "Copy to clipboard"),
|
||||
return nodx.Div(
|
||||
nodx.Class("inline-block tooltip tooltip-right"),
|
||||
nodx.Data("tip", "Copy to clipboard"),
|
||||
sc.script,
|
||||
html.Button(
|
||||
components.Classes{
|
||||
nodx.Button(
|
||||
nodx.ClassMap{
|
||||
"btn btn-neutral btn-square btn-ghost": true,
|
||||
"btn-sm": props.Size == SizeSm,
|
||||
"btn-lg": props.Size == SizeLg,
|
||||
},
|
||||
html.ID(id),
|
||||
html.Title("Copy to clipboard"),
|
||||
nodx.Id(id),
|
||||
nodx.TitleAttr("Copy to clipboard"),
|
||||
sc.copyEvent,
|
||||
lucide.Copy(),
|
||||
),
|
||||
@@ -74,8 +72,8 @@ func copyButtonScript(
|
||||
id string,
|
||||
textToCopy string,
|
||||
) struct {
|
||||
script gomponents.Node
|
||||
copyEvent gomponents.Node
|
||||
script nodx.Node
|
||||
copyEvent nodx.Node
|
||||
} {
|
||||
escapedTextToCopy := strings.ReplaceAll(textToCopy, "`", "\\`")
|
||||
|
||||
@@ -85,12 +83,12 @@ func copyButtonScript(
|
||||
escapedTextToCopy,
|
||||
)
|
||||
|
||||
script := gomponents.Raw(rawScript)
|
||||
copyEvent := gomponents.Attr("onclick", fmt.Sprintf("copy%s()", id))
|
||||
script := nodx.Raw(rawScript)
|
||||
copyEvent := nodx.Attr("onclick", fmt.Sprintf("copy%s()", id))
|
||||
|
||||
return struct {
|
||||
script gomponents.Node
|
||||
copyEvent gomponents.Node
|
||||
script nodx.Node
|
||||
copyEvent nodx.Node
|
||||
}{
|
||||
script: script,
|
||||
copyEvent: copyEvent,
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
package component
|
||||
|
||||
import (
|
||||
lucide "github.com/eduardolat/gomponents-lucide"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
lucide "github.com/nodxdev/nodxgo-lucide"
|
||||
)
|
||||
|
||||
type EmptyResultsParams struct {
|
||||
@@ -11,32 +10,32 @@ type EmptyResultsParams struct {
|
||||
Subtitle string
|
||||
}
|
||||
|
||||
func EmptyResults(params EmptyResultsParams) gomponents.Node {
|
||||
return html.Div(
|
||||
html.Class("flex flex-col justify-center items-center space-x-1"),
|
||||
lucide.FileSearch(html.Class("size-8")),
|
||||
gomponents.If(
|
||||
func EmptyResults(params EmptyResultsParams) nodx.Node {
|
||||
return nodx.Div(
|
||||
nodx.Class("flex flex-col justify-center items-center space-x-1"),
|
||||
lucide.FileSearch(nodx.Class("size-8")),
|
||||
nodx.If(
|
||||
params.Title != "",
|
||||
html.Span(
|
||||
html.Class("text-xl"),
|
||||
gomponents.Text(params.Title),
|
||||
nodx.SpanEl(
|
||||
nodx.Class("text-xl"),
|
||||
nodx.Text(params.Title),
|
||||
),
|
||||
),
|
||||
gomponents.If(
|
||||
nodx.If(
|
||||
params.Subtitle != "",
|
||||
html.Span(
|
||||
html.Class("text-base"),
|
||||
gomponents.Text(params.Subtitle),
|
||||
nodx.SpanEl(
|
||||
nodx.Class("text-base"),
|
||||
nodx.Text(params.Subtitle),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
func EmptyResultsTr(params EmptyResultsParams) gomponents.Node {
|
||||
return html.Tr(
|
||||
html.Td(
|
||||
html.ColSpan("100%"),
|
||||
html.Class("py-10"),
|
||||
func EmptyResultsTr(params EmptyResultsParams) nodx.Node {
|
||||
return nodx.Tr(
|
||||
nodx.Td(
|
||||
nodx.Colspan("100%"),
|
||||
nodx.Class("py-10"),
|
||||
EmptyResults(params),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
package component
|
||||
|
||||
import "github.com/maragudk/gomponents"
|
||||
|
||||
// GMap is a convenience function to render a gomponents.Group
|
||||
// with a map inside.
|
||||
func GMap[T any](ts []T, cb func(T) gomponents.Node) gomponents.Node {
|
||||
return gomponents.Group(gomponents.Map(ts, cb))
|
||||
}
|
||||
@@ -1,33 +1,32 @@
|
||||
package component
|
||||
|
||||
import (
|
||||
lucide "github.com/eduardolat/gomponents-lucide"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
lucide "github.com/nodxdev/nodxgo-lucide"
|
||||
)
|
||||
|
||||
type HelpButtonModalParams struct {
|
||||
ModalTitle string
|
||||
ModalSize size
|
||||
Children []gomponents.Node
|
||||
Children []nodx.Node
|
||||
}
|
||||
|
||||
func HelpButtonModal(params HelpButtonModalParams) gomponents.Node {
|
||||
func HelpButtonModal(params HelpButtonModalParams) nodx.Node {
|
||||
mo := Modal(ModalParams{
|
||||
Size: params.ModalSize,
|
||||
Title: params.ModalTitle,
|
||||
Content: params.Children,
|
||||
})
|
||||
|
||||
button := html.Button(
|
||||
button := nodx.Button(
|
||||
mo.OpenerAttr,
|
||||
html.Class("btn btn-neutral btn-ghost btn-circle btn-sm"),
|
||||
html.Type("button"),
|
||||
nodx.Class("btn btn-neutral btn-ghost btn-circle btn-sm"),
|
||||
nodx.Type("button"),
|
||||
lucide.CircleHelp(),
|
||||
)
|
||||
|
||||
return html.Div(
|
||||
html.Class("inline-block"),
|
||||
return nodx.Div(
|
||||
nodx.Class("inline-block"),
|
||||
mo.HTML,
|
||||
button,
|
||||
)
|
||||
|
||||
@@ -1,38 +1,37 @@
|
||||
package component
|
||||
|
||||
import (
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
)
|
||||
|
||||
// HxLoadingSm returns a small loading indicator.
|
||||
func HxLoadingSm(id ...string) gomponents.Node {
|
||||
func HxLoadingSm(id ...string) nodx.Node {
|
||||
return hxLoading(SizeSm, id...)
|
||||
}
|
||||
|
||||
// HxLoadingMd returns a loading indicator.
|
||||
func HxLoadingMd(id ...string) gomponents.Node {
|
||||
func HxLoadingMd(id ...string) nodx.Node {
|
||||
return hxLoading(SizeMd, id...)
|
||||
}
|
||||
|
||||
// HxLoadingLg returns a large loading indicator.
|
||||
func HxLoadingLg(id ...string) gomponents.Node {
|
||||
func HxLoadingLg(id ...string) nodx.Node {
|
||||
return hxLoading(SizeLg, id...)
|
||||
}
|
||||
|
||||
func hxLoading(size size, id ...string) gomponents.Node {
|
||||
func hxLoading(size size, id ...string) nodx.Node {
|
||||
pickedID := ""
|
||||
if len(id) > 0 {
|
||||
pickedID = id[0]
|
||||
}
|
||||
|
||||
return html.Div(
|
||||
gomponents.If(
|
||||
return nodx.Div(
|
||||
nodx.If(
|
||||
pickedID != "",
|
||||
html.ID(pickedID),
|
||||
nodx.Id(pickedID),
|
||||
),
|
||||
html.Class("htmx-indicator inline-block"),
|
||||
func() gomponents.Node {
|
||||
nodx.Class("htmx-indicator inline-block"),
|
||||
func() nodx.Node {
|
||||
switch size {
|
||||
case SizeSm:
|
||||
return SpinnerSm()
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
package component
|
||||
|
||||
import (
|
||||
lucide "github.com/eduardolat/gomponents-lucide"
|
||||
"github.com/google/uuid"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/components"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
lucide "github.com/nodxdev/nodxgo-lucide"
|
||||
)
|
||||
|
||||
type InputControlParams struct {
|
||||
@@ -19,11 +17,11 @@ type InputControlParams struct {
|
||||
Color color
|
||||
AutoComplete string
|
||||
Pattern string
|
||||
Children []gomponents.Node
|
||||
HelpButtonChildren []gomponents.Node
|
||||
Children []nodx.Node
|
||||
HelpButtonChildren []nodx.Node
|
||||
}
|
||||
|
||||
func InputControl(params InputControlParams) gomponents.Node {
|
||||
func InputControl(params InputControlParams) nodx.Node {
|
||||
id := params.ID
|
||||
if id == "" {
|
||||
id = "input-control-" + uuid.NewString()
|
||||
@@ -33,23 +31,23 @@ func InputControl(params InputControlParams) gomponents.Node {
|
||||
params.Type = InputTypeText
|
||||
}
|
||||
|
||||
return html.Div(
|
||||
components.Classes{
|
||||
return nodx.Div(
|
||||
nodx.ClassMap{
|
||||
"form-control w-full": true,
|
||||
getTextColorClass(params.Color): true,
|
||||
},
|
||||
html.Div(
|
||||
html.Class("label flex justify-start"),
|
||||
html.Label(
|
||||
html.For(id),
|
||||
html.Class("flex justify-start items-center space-x-1"),
|
||||
nodx.Div(
|
||||
nodx.Class("label flex justify-start"),
|
||||
nodx.LabelEl(
|
||||
nodx.For(id),
|
||||
nodx.Class("flex justify-start items-center space-x-1"),
|
||||
SpanText(params.Label),
|
||||
gomponents.If(
|
||||
nodx.If(
|
||||
params.Required,
|
||||
lucide.Asterisk(html.Class("text-error")),
|
||||
lucide.Asterisk(nodx.Class("text-error")),
|
||||
),
|
||||
),
|
||||
gomponents.If(
|
||||
nodx.If(
|
||||
len(params.HelpButtonChildren) > 0,
|
||||
HelpButtonModal(HelpButtonModalParams{
|
||||
ModalTitle: params.Label,
|
||||
@@ -57,34 +55,34 @@ func InputControl(params InputControlParams) gomponents.Node {
|
||||
}),
|
||||
),
|
||||
),
|
||||
html.Input(
|
||||
components.Classes{
|
||||
nodx.Input(
|
||||
nodx.ClassMap{
|
||||
"input input-bordered w-full": true,
|
||||
getInputColorClass(params.Color): true,
|
||||
},
|
||||
html.ID(id),
|
||||
html.Type(params.Type.Value),
|
||||
html.Name(params.Name),
|
||||
html.Placeholder(params.Placeholder),
|
||||
gomponents.If(
|
||||
nodx.Id(id),
|
||||
nodx.Type(params.Type.Value),
|
||||
nodx.Name(params.Name),
|
||||
nodx.Placeholder(params.Placeholder),
|
||||
nodx.If(
|
||||
params.Required,
|
||||
html.Required(),
|
||||
nodx.Required(""),
|
||||
),
|
||||
gomponents.If(
|
||||
nodx.If(
|
||||
params.AutoComplete != "",
|
||||
html.AutoComplete(params.AutoComplete),
|
||||
nodx.Autocomplete(params.AutoComplete),
|
||||
),
|
||||
gomponents.If(
|
||||
nodx.If(
|
||||
params.Pattern != "",
|
||||
html.Pattern(params.Pattern),
|
||||
nodx.Pattern(params.Pattern),
|
||||
),
|
||||
gomponents.Group(params.Children),
|
||||
nodx.Group(params.Children...),
|
||||
),
|
||||
gomponents.If(
|
||||
nodx.If(
|
||||
params.HelpText != "",
|
||||
html.Label(
|
||||
html.Class("label"),
|
||||
html.For(id),
|
||||
nodx.LabelEl(
|
||||
nodx.Class("label"),
|
||||
nodx.For(id),
|
||||
SpanText(params.HelpText),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,25 +1,23 @@
|
||||
package component
|
||||
|
||||
import (
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/components"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
)
|
||||
|
||||
func Logotype() gomponents.Node {
|
||||
return html.Div(
|
||||
components.Classes{
|
||||
func Logotype() nodx.Node {
|
||||
return nodx.Div(
|
||||
nodx.ClassMap{
|
||||
"inline space-x-2 select-none": true,
|
||||
"flex justify-start items-center": true,
|
||||
},
|
||||
html.Img(
|
||||
html.Class("w-[60px] h-auto"),
|
||||
html.Src("/images/logo.png"),
|
||||
html.Alt("PG Back Web"),
|
||||
nodx.Img(
|
||||
nodx.Class("w-[60px] h-auto"),
|
||||
nodx.Src("/images/logo.png"),
|
||||
nodx.Alt("PG Back Web"),
|
||||
),
|
||||
html.Span(
|
||||
html.Class("text-2xl font-bold"),
|
||||
gomponents.Text("PG Back Web"),
|
||||
nodx.SpanEl(
|
||||
nodx.Class("text-2xl font-bold"),
|
||||
nodx.Text("PG Back Web"),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3,12 +3,10 @@ package component
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
lucide "github.com/eduardolat/gomponents-lucide"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/alpine"
|
||||
"github.com/google/uuid"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/components"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
lucide "github.com/nodxdev/nodxgo-lucide"
|
||||
)
|
||||
|
||||
// ModalParams are the props for the Modal component.
|
||||
@@ -16,7 +14,7 @@ type ModalParams struct {
|
||||
// ID is the ID of the modal dialog. If empty, a random ID will be generated.
|
||||
ID string
|
||||
// Content is the content of the modal dialog.
|
||||
Content []gomponents.Node
|
||||
Content []nodx.Node
|
||||
// Size is the size of the modal dialog.
|
||||
// Can be "sm", "md", and "lg".
|
||||
// The default is "md".
|
||||
@@ -26,7 +24,7 @@ type ModalParams struct {
|
||||
Title string
|
||||
// TitleNode is the title of the modal dialog.
|
||||
// If you need only a string, use Title instead.
|
||||
TitleNode gomponents.Node
|
||||
TitleNode nodx.Node
|
||||
// HTMXIndicator is an optional ID of an HTMX indicator that
|
||||
// should be inserted in the modal header.
|
||||
HTMXIndicator string
|
||||
@@ -34,10 +32,10 @@ type ModalParams struct {
|
||||
|
||||
// ModalResult is the result of creating a modal dialog.
|
||||
type ModalResult struct {
|
||||
// HTML is the modal dialog HTML.
|
||||
HTML gomponents.Node
|
||||
// HTML is the modal dialog nodx.
|
||||
HTML nodx.Node
|
||||
// OpenerAttr is the attribute to add to the element that opens the modal dialog.
|
||||
OpenerAttr gomponents.Node
|
||||
OpenerAttr nodx.Node
|
||||
}
|
||||
|
||||
// Modal renders a modal dialog.
|
||||
@@ -49,11 +47,11 @@ func Modal(params ModalParams) ModalResult {
|
||||
|
||||
openEventName := fmt.Sprintf("%s_open", id)
|
||||
closeEventName := fmt.Sprintf("%s_close", id)
|
||||
openerAttr := gomponents.Attr(
|
||||
openerAttr := nodx.Attr(
|
||||
"onClick",
|
||||
"event.preventDefault(); window.dispatchEvent(new Event('"+openEventName+"'));",
|
||||
)
|
||||
closerAttr := gomponents.Attr(
|
||||
closerAttr := nodx.Attr(
|
||||
"onClick",
|
||||
"event.preventDefault(); window.dispatchEvent(new Event('"+closeEventName+"'));",
|
||||
)
|
||||
@@ -68,23 +66,23 @@ func Modal(params ModalParams) ModalResult {
|
||||
|
||||
hasHTMXIndicator := params.HTMXIndicator != ""
|
||||
|
||||
content := html.Div(
|
||||
content := nodx.Div(
|
||||
alpine.XData(`{}`),
|
||||
alpine.XOn(fmt.Sprintf("%s.window", openEventName), openCode),
|
||||
alpine.XOn(fmt.Sprintf("%s.window", closeEventName), closeCode),
|
||||
alpine.XOn("keyup.escape.window", closeCode),
|
||||
|
||||
html.ID(id),
|
||||
components.Classes{
|
||||
nodx.Id(id),
|
||||
nodx.ClassMap{
|
||||
"hidden": true,
|
||||
"!p-0 !m-0 w-[100dvw] h-[100dvh]": true,
|
||||
"fixed left-0 top-0 z-[1000]": true,
|
||||
},
|
||||
|
||||
// Backdrop
|
||||
html.Div(
|
||||
nodx.Div(
|
||||
closerAttr,
|
||||
components.Classes{
|
||||
nodx.ClassMap{
|
||||
"bg-black opacity-25": true,
|
||||
"!w-full !h-full": true,
|
||||
"z-[1001]": true,
|
||||
@@ -92,8 +90,8 @@ func Modal(params ModalParams) ModalResult {
|
||||
),
|
||||
|
||||
// Dialog
|
||||
html.Div(
|
||||
components.Classes{
|
||||
nodx.Div(
|
||||
nodx.ClassMap{
|
||||
"absolute z-[1002] top-[50%] left-[50%]": true,
|
||||
"translate-y-[-50%] translate-x-[-50%]": true,
|
||||
"max-w-[calc(100dvw-30px)] max-h-[85dvh]": true,
|
||||
@@ -105,46 +103,46 @@ func Modal(params ModalParams) ModalResult {
|
||||
"w-[800px]": size == SizeLg,
|
||||
},
|
||||
|
||||
html.Div(
|
||||
components.Classes{
|
||||
nodx.Div(
|
||||
nodx.ClassMap{
|
||||
"w-full sticky top-0 right-0 z-[1003] bg-base-100": true,
|
||||
"flex items-center justify-between": true,
|
||||
"border-b border-base-300 px-4 py-3": true,
|
||||
},
|
||||
|
||||
html.Div(
|
||||
gomponents.If(
|
||||
nodx.Div(
|
||||
nodx.If(
|
||||
params.TitleNode != nil,
|
||||
params.TitleNode,
|
||||
),
|
||||
|
||||
gomponents.If(
|
||||
nodx.If(
|
||||
params.Title != "",
|
||||
html.Span(
|
||||
html.Class("text-xl font-bold desk:text-2xl"),
|
||||
gomponents.Text(params.Title),
|
||||
nodx.SpanEl(
|
||||
nodx.Class("text-xl font-bold desk:text-2xl"),
|
||||
nodx.Text(params.Title),
|
||||
),
|
||||
),
|
||||
|
||||
gomponents.If(
|
||||
nodx.If(
|
||||
hasHTMXIndicator,
|
||||
html.Div(
|
||||
html.Class("inline-flex h-full items-center pl-2"),
|
||||
nodx.Div(
|
||||
nodx.Class("inline-flex h-full items-center pl-2"),
|
||||
HxLoadingSm(params.HTMXIndicator),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
html.Button(
|
||||
html.Class("btn btn-circle btn-ghost btn-sm"),
|
||||
lucide.X(html.Class("size-6")),
|
||||
nodx.Button(
|
||||
nodx.Class("btn btn-circle btn-ghost btn-sm"),
|
||||
lucide.X(nodx.Class("size-6")),
|
||||
closerAttr,
|
||||
),
|
||||
),
|
||||
|
||||
html.Div(
|
||||
html.Class("p-4"),
|
||||
gomponents.Group(params.Content),
|
||||
nodx.Div(
|
||||
nodx.Class("p-4"),
|
||||
nodx.Group(params.Content...),
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -152,7 +150,7 @@ func Modal(params ModalParams) ModalResult {
|
||||
content = alpine.Template(
|
||||
alpine.XData(""),
|
||||
alpine.XTeleport("body"),
|
||||
html.Div(content),
|
||||
nodx.Div(content),
|
||||
)
|
||||
|
||||
return ModalResult{
|
||||
|
||||
@@ -1,50 +1,48 @@
|
||||
package component
|
||||
|
||||
import (
|
||||
lucide "github.com/eduardolat/gomponents-lucide"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/alpine"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/components"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
lucide "github.com/nodxdev/nodxgo-lucide"
|
||||
)
|
||||
|
||||
func OptionsDropdown(children ...gomponents.Node) gomponents.Node {
|
||||
return html.Div(
|
||||
html.Class("inline-block"),
|
||||
func OptionsDropdown(children ...nodx.Node) nodx.Node {
|
||||
return nodx.Div(
|
||||
nodx.Class("inline-block"),
|
||||
alpine.XData("alpineOptionsDropdown()"),
|
||||
alpine.XOn("mouseenter", "open()"),
|
||||
alpine.XOn("mouseleave", "close()"),
|
||||
html.Button(
|
||||
nodx.Button(
|
||||
alpine.XRef("button"),
|
||||
html.Class("btn btn-sm btn-ghost btn-square"),
|
||||
nodx.Class("btn btn-sm btn-ghost btn-square"),
|
||||
alpine.XBind("class", "isOpen ? 'btn-active' : ''"),
|
||||
lucide.EllipsisVertical(
|
||||
html.Class("transition-transform"),
|
||||
nodx.Class("transition-transform"),
|
||||
alpine.XBind("class", "isOpen ? 'rotate-90' : ''"),
|
||||
),
|
||||
),
|
||||
html.Div(
|
||||
nodx.Div(
|
||||
alpine.XRef("content"),
|
||||
components.Classes{
|
||||
nodx.ClassMap{
|
||||
"fixed hidden": true,
|
||||
"bg-base-100 rounded-box border border-base-200": true,
|
||||
"z-40 max-w-[250px] p-2 shadow-md": true,
|
||||
},
|
||||
gomponents.Group(children),
|
||||
nodx.Group(children...),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
func OptionsDropdownButton(children ...gomponents.Node) gomponents.Node {
|
||||
return html.Button(
|
||||
html.Class("btn btn-neutral btn-ghost btn-sm w-full flex justify-start"),
|
||||
gomponents.Group(children),
|
||||
func OptionsDropdownButton(children ...nodx.Node) nodx.Node {
|
||||
return nodx.Button(
|
||||
nodx.Class("btn btn-neutral btn-ghost btn-sm w-full flex justify-start"),
|
||||
nodx.Group(children...),
|
||||
)
|
||||
}
|
||||
|
||||
func OptionsDropdownA(children ...gomponents.Node) gomponents.Node {
|
||||
return html.A(
|
||||
html.Class("btn btn-neutral btn-ghost btn-sm w-full flex justify-start"),
|
||||
gomponents.Group(children),
|
||||
func OptionsDropdownA(children ...nodx.Node) nodx.Node {
|
||||
return nodx.A(
|
||||
nodx.Class("btn btn-neutral btn-ghost btn-sm w-full flex justify-start"),
|
||||
nodx.Group(children...),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4,20 +4,19 @@ import (
|
||||
"database/sql"
|
||||
|
||||
"github.com/eduardolat/pgbackweb/internal/integration/postgres"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
)
|
||||
|
||||
func PGVersionSelectOptions(selectedVersion sql.NullString) gomponents.Node {
|
||||
return GMap(
|
||||
func PGVersionSelectOptions(selectedVersion sql.NullString) nodx.Node {
|
||||
return nodx.Map(
|
||||
postgres.PGVersions,
|
||||
func(pgVersion postgres.PGVersion) gomponents.Node {
|
||||
return html.Option(
|
||||
html.Value(pgVersion.Value.Version),
|
||||
gomponents.Textf("PostgreSQL %s", pgVersion.Value.Version),
|
||||
gomponents.If(
|
||||
func(pgVersion postgres.PGVersion) nodx.Node {
|
||||
return nodx.Option(
|
||||
nodx.Value(pgVersion.Value.Version),
|
||||
nodx.Textf("PostgreSQL %s", pgVersion.Value.Version),
|
||||
nodx.If(
|
||||
selectedVersion.Valid && selectedVersion.String == pgVersion.Value.Version,
|
||||
html.Selected(),
|
||||
nodx.Selected(""),
|
||||
),
|
||||
)
|
||||
},
|
||||
|
||||
@@ -4,12 +4,10 @@ import (
|
||||
"database/sql"
|
||||
|
||||
"github.com/eduardolat/pgbackweb/internal/util/timeutil"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/components"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
)
|
||||
|
||||
func Ping(color color) gomponents.Node {
|
||||
func Ping(color color) nodx.Node {
|
||||
if color.Value == "" {
|
||||
color = ColorNeutral
|
||||
}
|
||||
@@ -34,17 +32,17 @@ func Ping(color color) gomponents.Node {
|
||||
bgClass = "bg-error"
|
||||
}
|
||||
|
||||
return html.Span(
|
||||
html.Class("relative flex h-3 w-3"),
|
||||
html.Span(
|
||||
components.Classes{
|
||||
return nodx.SpanEl(
|
||||
nodx.Class("relative flex h-3 w-3"),
|
||||
nodx.SpanEl(
|
||||
nodx.ClassMap{
|
||||
"absolute inline-flex h-full w-full": true,
|
||||
"animate-ping rounded-full opacity-75": true,
|
||||
bgClass: true,
|
||||
},
|
||||
),
|
||||
html.Span(
|
||||
components.Classes{
|
||||
nodx.SpanEl(
|
||||
nodx.ClassMap{
|
||||
"relative inline-flex rounded-full h-3 w-3": true,
|
||||
bgClass: true,
|
||||
},
|
||||
@@ -52,23 +50,23 @@ func Ping(color color) gomponents.Node {
|
||||
)
|
||||
}
|
||||
|
||||
func IsActivePing(isActive bool) gomponents.Node {
|
||||
func IsActivePing(isActive bool) nodx.Node {
|
||||
pingColor := ColorSuccess
|
||||
if !isActive {
|
||||
pingColor = ColorError
|
||||
}
|
||||
|
||||
return html.Div(
|
||||
html.Class("tooltip tooltip-right"),
|
||||
gomponents.If(isActive, html.Data("tip", "Active")),
|
||||
gomponents.If(!isActive, html.Data("tip", "Inactive")),
|
||||
return nodx.Div(
|
||||
nodx.Class("tooltip tooltip-right"),
|
||||
nodx.If(isActive, nodx.Data("tip", "Active")),
|
||||
nodx.If(!isActive, nodx.Data("tip", "Inactive")),
|
||||
Ping(pingColor),
|
||||
)
|
||||
}
|
||||
|
||||
func HealthStatusPing(
|
||||
testOk sql.NullBool, testError sql.NullString, lastTestAt sql.NullTime,
|
||||
) gomponents.Node {
|
||||
) nodx.Node {
|
||||
pingColor := ColorWarning
|
||||
if testOk.Valid {
|
||||
if testOk.Bool {
|
||||
@@ -78,7 +76,7 @@ func HealthStatusPing(
|
||||
}
|
||||
}
|
||||
|
||||
var moOpenerAttr, moHTML gomponents.Node
|
||||
var moOpenerAttr, moHTML nodx.Node
|
||||
|
||||
if testOk.Valid {
|
||||
statusText := "Healthy"
|
||||
@@ -89,37 +87,37 @@ func HealthStatusPing(
|
||||
mo := Modal(ModalParams{
|
||||
Size: SizeSm,
|
||||
Title: "Health check details",
|
||||
Content: []gomponents.Node{
|
||||
html.Div(
|
||||
html.Class("overflow-x-auto"),
|
||||
html.Table(
|
||||
html.Class("table [&_th]:text-nowrap"),
|
||||
html.Tr(
|
||||
html.Th(SpanText("Status")),
|
||||
html.Td(SpanText(statusText)),
|
||||
Content: []nodx.Node{
|
||||
nodx.Div(
|
||||
nodx.Class("overflow-x-auto"),
|
||||
nodx.Table(
|
||||
nodx.Class("table [&_th]:text-nowrap"),
|
||||
nodx.Tr(
|
||||
nodx.Th(SpanText("Status")),
|
||||
nodx.Td(SpanText(statusText)),
|
||||
),
|
||||
gomponents.If(
|
||||
nodx.If(
|
||||
testError.Valid && testError.String != "",
|
||||
html.Tr(
|
||||
html.Th(SpanText("Error")),
|
||||
html.Td(
|
||||
html.Class("break-all"),
|
||||
nodx.Tr(
|
||||
nodx.Th(SpanText("Error")),
|
||||
nodx.Td(
|
||||
nodx.Class("break-all"),
|
||||
SpanText(testError.String),
|
||||
),
|
||||
),
|
||||
),
|
||||
gomponents.If(
|
||||
nodx.If(
|
||||
lastTestAt.Valid,
|
||||
html.Tr(
|
||||
html.Th(SpanText("Tested at")),
|
||||
html.Td(SpanText(
|
||||
nodx.Tr(
|
||||
nodx.Th(SpanText("Tested at")),
|
||||
nodx.Td(SpanText(
|
||||
lastTestAt.Time.Local().Format(timeutil.LayoutYYYYMMDDHHMMSSPretty),
|
||||
)),
|
||||
),
|
||||
),
|
||||
html.Tr(
|
||||
html.Td(
|
||||
html.ColSpan("2"),
|
||||
nodx.Tr(
|
||||
nodx.Td(
|
||||
nodx.Colspan("2"),
|
||||
PText(`
|
||||
The health check runs automatically every 10 minutes, when
|
||||
PG Back Web starts, and when you click the "Test connection"
|
||||
@@ -146,13 +144,13 @@ func HealthStatusPing(
|
||||
return "Waiting for next test"
|
||||
}()
|
||||
|
||||
return html.Div(
|
||||
html.Class("tooltip tooltip-right"),
|
||||
html.Data("tip", tooltipText),
|
||||
return nodx.Div(
|
||||
nodx.Class("tooltip tooltip-right"),
|
||||
nodx.Data("tip", tooltipText),
|
||||
moHTML,
|
||||
html.Span(
|
||||
nodx.SpanEl(
|
||||
moOpenerAttr,
|
||||
html.Class("cursor-pointer"),
|
||||
nodx.Class("cursor-pointer"),
|
||||
Ping(pingColor),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -3,14 +3,13 @@ package component
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
lucide "github.com/eduardolat/gomponents-lucide"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
lucide "github.com/nodxdev/nodxgo-lucide"
|
||||
)
|
||||
|
||||
func PrettyDestinationName(
|
||||
isLocal bool, destinationName sql.NullString,
|
||||
) gomponents.Node {
|
||||
) nodx.Node {
|
||||
icon := lucide.Cloud
|
||||
if !destinationName.Valid {
|
||||
destinationName = sql.NullString{
|
||||
@@ -27,8 +26,8 @@ func PrettyDestinationName(
|
||||
}
|
||||
}
|
||||
|
||||
return html.Span(
|
||||
html.Class("inline flex justify-start items-center space-x-1 font-mono"),
|
||||
return nodx.SpanEl(
|
||||
nodx.Class("inline flex justify-start items-center space-x-1 font-mono"),
|
||||
icon(),
|
||||
SpanText(destinationName.String),
|
||||
)
|
||||
|
||||
@@ -4,8 +4,7 @@ import (
|
||||
"database/sql"
|
||||
|
||||
"github.com/eduardolat/pgbackweb/internal/util/strutil"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
)
|
||||
|
||||
// PrettyFileSize pretty prints a file size (in bytes) to a human-readable format.
|
||||
@@ -14,10 +13,10 @@ import (
|
||||
// e.g. 1024 -> 1 KB
|
||||
func PrettyFileSize(
|
||||
size sql.NullInt64,
|
||||
) gomponents.Node {
|
||||
return gomponents.If(
|
||||
) nodx.Node {
|
||||
return nodx.If(
|
||||
size.Valid,
|
||||
html.Span(
|
||||
nodx.SpanEl(
|
||||
SpanText(strutil.FormatFileSize(size.Int64)),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -3,20 +3,20 @@ package component
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/maragudk/gomponents"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
)
|
||||
|
||||
// RenderableGroup renders a group of nodes without a parent element.
|
||||
//
|
||||
// This is because gomponents.Group() cannot be directly rendered and
|
||||
// This is because nodx.Group() cannot be directly rendered and
|
||||
// needs to be wrapped in a parent element.
|
||||
func RenderableGroup(children []gomponents.Node) gomponents.Node {
|
||||
func RenderableGroup(children []nodx.Node) nodx.Node {
|
||||
buf := bytes.Buffer{}
|
||||
for _, child := range children {
|
||||
err := child.Render(&buf)
|
||||
if err != nil {
|
||||
return gomponents.Raw("Error rendering group")
|
||||
return nodx.Raw("Error rendering group")
|
||||
}
|
||||
}
|
||||
return gomponents.Raw(buf.String())
|
||||
return nodx.Raw(buf.String())
|
||||
}
|
||||
|
||||
@@ -4,16 +4,15 @@ import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestRenderableGroupRenderer(t *testing.T) {
|
||||
t.Run("renders a group of string nodes without a parent element", func(t *testing.T) {
|
||||
gotRenderer := RenderableGroup([]gomponents.Node{
|
||||
gomponents.Text("foo"),
|
||||
gomponents.Text("bar"),
|
||||
gotRenderer := RenderableGroup([]nodx.Node{
|
||||
nodx.Text("foo"),
|
||||
nodx.Text("bar"),
|
||||
})
|
||||
|
||||
got := bytes.Buffer{}
|
||||
@@ -26,12 +25,12 @@ func TestRenderableGroupRenderer(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("renders a group of tag nodes without a parent element", func(t *testing.T) {
|
||||
gotRenderer := RenderableGroup([]gomponents.Node{
|
||||
html.Span(
|
||||
gomponents.Text("foo"),
|
||||
gotRenderer := RenderableGroup([]nodx.Node{
|
||||
nodx.SpanEl(
|
||||
nodx.Text("foo"),
|
||||
),
|
||||
html.P(
|
||||
gomponents.Text("bar"),
|
||||
nodx.P(
|
||||
nodx.Text("bar"),
|
||||
),
|
||||
})
|
||||
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
package component
|
||||
|
||||
import (
|
||||
lucide "github.com/eduardolat/gomponents-lucide"
|
||||
"github.com/google/uuid"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/components"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
lucide "github.com/nodxdev/nodxgo-lucide"
|
||||
)
|
||||
|
||||
type SelectControlParams struct {
|
||||
@@ -17,33 +15,33 @@ type SelectControlParams struct {
|
||||
HelpText string
|
||||
Color color
|
||||
AutoComplete string
|
||||
Children []gomponents.Node
|
||||
HelpButtonChildren []gomponents.Node
|
||||
Children []nodx.Node
|
||||
HelpButtonChildren []nodx.Node
|
||||
}
|
||||
|
||||
func SelectControl(params SelectControlParams) gomponents.Node {
|
||||
func SelectControl(params SelectControlParams) nodx.Node {
|
||||
id := params.ID
|
||||
if id == "" {
|
||||
id = "select-control-" + uuid.NewString()
|
||||
}
|
||||
|
||||
return html.Div(
|
||||
components.Classes{
|
||||
return nodx.Div(
|
||||
nodx.ClassMap{
|
||||
"form-control w-full": true,
|
||||
getTextColorClass(params.Color): true,
|
||||
},
|
||||
html.Div(
|
||||
html.Class("label flex justify-start"),
|
||||
html.Label(
|
||||
html.For(id),
|
||||
html.Class("flex justify-start items-center space-x-1"),
|
||||
nodx.Div(
|
||||
nodx.Class("label flex justify-start"),
|
||||
nodx.LabelEl(
|
||||
nodx.For(id),
|
||||
nodx.Class("flex justify-start items-center space-x-1"),
|
||||
SpanText(params.Label),
|
||||
gomponents.If(
|
||||
nodx.If(
|
||||
params.Required,
|
||||
lucide.Asterisk(html.Class("text-error")),
|
||||
lucide.Asterisk(nodx.Class("text-error")),
|
||||
),
|
||||
),
|
||||
gomponents.If(
|
||||
nodx.If(
|
||||
len(params.HelpButtonChildren) > 0,
|
||||
HelpButtonModal(HelpButtonModalParams{
|
||||
ModalTitle: params.Label,
|
||||
@@ -51,40 +49,40 @@ func SelectControl(params SelectControlParams) gomponents.Node {
|
||||
}),
|
||||
),
|
||||
),
|
||||
html.Select(
|
||||
components.Classes{
|
||||
nodx.Select(
|
||||
nodx.ClassMap{
|
||||
"w-full": true,
|
||||
},
|
||||
html.ID(id),
|
||||
html.Name(params.Name),
|
||||
gomponents.If(
|
||||
nodx.Id(id),
|
||||
nodx.Name(params.Name),
|
||||
nodx.If(
|
||||
params.Required,
|
||||
html.Required(),
|
||||
nodx.Required(""),
|
||||
),
|
||||
gomponents.If(
|
||||
nodx.If(
|
||||
params.AutoComplete != "",
|
||||
html.AutoComplete(params.AutoComplete),
|
||||
nodx.Autocomplete(params.AutoComplete),
|
||||
),
|
||||
gomponents.If(
|
||||
nodx.If(
|
||||
params.Placeholder != "",
|
||||
html.Option(
|
||||
html.Value(""),
|
||||
html.Disabled(),
|
||||
html.Selected(),
|
||||
gomponents.Text(params.Placeholder),
|
||||
nodx.Option(
|
||||
nodx.Value(""),
|
||||
nodx.Disabled(""),
|
||||
nodx.Selected(""),
|
||||
nodx.Text(params.Placeholder),
|
||||
),
|
||||
),
|
||||
gomponents.Group(params.Children),
|
||||
nodx.Group(params.Children...),
|
||||
),
|
||||
gomponents.If(
|
||||
nodx.If(
|
||||
params.HelpText != "",
|
||||
html.Label(
|
||||
html.Class("label"),
|
||||
html.For(id),
|
||||
nodx.LabelEl(
|
||||
nodx.Class("label"),
|
||||
nodx.For(id),
|
||||
SpanText(params.HelpText),
|
||||
),
|
||||
),
|
||||
html.Script(gomponents.Raw(`
|
||||
nodx.Script(nodx.Raw(`
|
||||
new SlimSelect({select: '#`+id+`'})
|
||||
`)),
|
||||
)
|
||||
|
||||
@@ -1,23 +1,22 @@
|
||||
package component
|
||||
|
||||
import (
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
)
|
||||
|
||||
func SkeletonTr(rows int) gomponents.Node {
|
||||
rs := make([]gomponents.Node, rows)
|
||||
func SkeletonTr(rows int) nodx.Node {
|
||||
rs := make([]nodx.Node, rows)
|
||||
for i := range rs {
|
||||
rs[i] = html.Tr(
|
||||
html.Td(
|
||||
html.ColSpan("100%"),
|
||||
html.Div(
|
||||
html.Class("animate-pulse h-4 w-full bg-base-300 rounded-badge"),
|
||||
rs[i] = nodx.Tr(
|
||||
nodx.Td(
|
||||
nodx.Colspan("100%"),
|
||||
nodx.Div(
|
||||
nodx.Class("animate-pulse h-4 w-full bg-base-300 rounded-badge"),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
return gomponents.Group(rs)
|
||||
return nodx.Group(rs...)
|
||||
|
||||
}
|
||||
|
||||
@@ -3,14 +3,12 @@ package component
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
lucide "github.com/eduardolat/gomponents-lucide"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/components"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
lucide "github.com/nodxdev/nodxgo-lucide"
|
||||
)
|
||||
|
||||
func spinner(size size) gomponents.Node {
|
||||
return lucide.LoaderCircle(components.Classes{
|
||||
func spinner(size size) nodx.Node {
|
||||
return lucide.LoaderCircle(nodx.ClassMap{
|
||||
"animate-spin inline-block": true,
|
||||
"size-5": size == SizeSm,
|
||||
"size-8": size == SizeMd,
|
||||
@@ -18,30 +16,30 @@ func spinner(size size) gomponents.Node {
|
||||
})
|
||||
}
|
||||
|
||||
func SpinnerSm() gomponents.Node {
|
||||
func SpinnerSm() nodx.Node {
|
||||
return spinner(SizeSm)
|
||||
}
|
||||
|
||||
func SpinnerMd() gomponents.Node {
|
||||
func SpinnerMd() nodx.Node {
|
||||
return spinner(SizeMd)
|
||||
}
|
||||
|
||||
func SpinnerLg() gomponents.Node {
|
||||
func SpinnerLg() nodx.Node {
|
||||
return spinner(SizeLg)
|
||||
}
|
||||
|
||||
func spinnerContainer(size size, height string) gomponents.Node {
|
||||
return html.Div(
|
||||
components.Classes{
|
||||
func spinnerContainer(size size, height string) nodx.Node {
|
||||
return nodx.Div(
|
||||
nodx.ClassMap{
|
||||
"flex justify-center": true,
|
||||
"items-center w-full": true,
|
||||
},
|
||||
html.Style(fmt.Sprintf("height: %s;", height)),
|
||||
nodx.StyleAttr(fmt.Sprintf("height: %s;", height)),
|
||||
spinner(size),
|
||||
)
|
||||
}
|
||||
|
||||
func SpinnerContainerSm(height ...string) gomponents.Node {
|
||||
func SpinnerContainerSm(height ...string) nodx.Node {
|
||||
pickedHeight := "300px"
|
||||
if len(height) > 0 {
|
||||
pickedHeight = height[0]
|
||||
@@ -49,7 +47,7 @@ func SpinnerContainerSm(height ...string) gomponents.Node {
|
||||
return spinnerContainer(SizeSm, pickedHeight)
|
||||
}
|
||||
|
||||
func SpinnerContainerMd(height ...string) gomponents.Node {
|
||||
func SpinnerContainerMd(height ...string) nodx.Node {
|
||||
pickedHeight := "300px"
|
||||
if len(height) > 0 {
|
||||
pickedHeight = height[0]
|
||||
@@ -57,7 +55,7 @@ func SpinnerContainerMd(height ...string) gomponents.Node {
|
||||
return spinnerContainer(SizeMd, pickedHeight)
|
||||
}
|
||||
|
||||
func SpinnerContainerLg(height ...string) gomponents.Node {
|
||||
func SpinnerContainerLg(height ...string) nodx.Node {
|
||||
pickedHeight := "300px"
|
||||
if len(height) > 0 {
|
||||
pickedHeight = height[0]
|
||||
|
||||
@@ -1,27 +1,25 @@
|
||||
package component
|
||||
|
||||
import (
|
||||
lucide "github.com/eduardolat/gomponents-lucide"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/alpine"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/components"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
lucide "github.com/nodxdev/nodxgo-lucide"
|
||||
)
|
||||
|
||||
func StarOnGithub(size size) gomponents.Node {
|
||||
return html.A(
|
||||
func StarOnGithub(size size) nodx.Node {
|
||||
return nodx.A(
|
||||
alpine.XData("alpineStarOnGithub()"),
|
||||
alpine.XCloak(),
|
||||
components.Classes{
|
||||
nodx.ClassMap{
|
||||
"btn btn-neutral": true,
|
||||
"btn-sm": size == SizeSm,
|
||||
"btn-lg": size == SizeLg,
|
||||
},
|
||||
html.Href("https://github.com/eduardolat/pgbackweb"),
|
||||
html.Target("_blank"),
|
||||
nodx.Href("https://github.com/eduardolat/pgbackweb"),
|
||||
nodx.Target("_blank"),
|
||||
lucide.Github(),
|
||||
SpanText("Star on Github"),
|
||||
html.Span(
|
||||
nodx.SpanEl(
|
||||
alpine.XShow("stars"),
|
||||
alpine.XText("'( ' + stars + ' )'"),
|
||||
),
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
package component
|
||||
|
||||
import (
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/components"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
)
|
||||
|
||||
func StatusBadge(status string) gomponents.Node {
|
||||
func StatusBadge(status string) nodx.Node {
|
||||
class := ""
|
||||
switch status {
|
||||
case "running":
|
||||
@@ -21,11 +19,11 @@ func StatusBadge(status string) gomponents.Node {
|
||||
class = "badge-neutral"
|
||||
}
|
||||
|
||||
return html.Span(
|
||||
components.Classes{
|
||||
return nodx.SpanEl(
|
||||
nodx.ClassMap{
|
||||
"badge": true,
|
||||
class: true,
|
||||
},
|
||||
gomponents.Text(status),
|
||||
nodx.Text(status),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
package component
|
||||
|
||||
import (
|
||||
lucide "github.com/eduardolat/gomponents-lucide"
|
||||
"github.com/google/uuid"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/components"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
lucide "github.com/nodxdev/nodxgo-lucide"
|
||||
)
|
||||
|
||||
type TextareaControlParams struct {
|
||||
@@ -18,33 +16,33 @@ type TextareaControlParams struct {
|
||||
Color color
|
||||
AutoComplete string
|
||||
Pattern string
|
||||
Children []gomponents.Node
|
||||
HelpButtonChildren []gomponents.Node
|
||||
Children []nodx.Node
|
||||
HelpButtonChildren []nodx.Node
|
||||
}
|
||||
|
||||
func TextareaControl(params TextareaControlParams) gomponents.Node {
|
||||
func TextareaControl(params TextareaControlParams) nodx.Node {
|
||||
id := params.ID
|
||||
if id == "" {
|
||||
id = "textarea-control-" + uuid.NewString()
|
||||
}
|
||||
|
||||
return html.Div(
|
||||
components.Classes{
|
||||
return nodx.Div(
|
||||
nodx.ClassMap{
|
||||
"form-control w-full": true,
|
||||
getTextColorClass(params.Color): true,
|
||||
},
|
||||
html.Div(
|
||||
html.Class("label flex justify-start"),
|
||||
html.Label(
|
||||
html.For(id),
|
||||
html.Class("flex justify-start items-center space-x-1"),
|
||||
nodx.Div(
|
||||
nodx.Class("label flex justify-start"),
|
||||
nodx.LabelEl(
|
||||
nodx.For(id),
|
||||
nodx.Class("flex justify-start items-center space-x-1"),
|
||||
SpanText(params.Label),
|
||||
gomponents.If(
|
||||
nodx.If(
|
||||
params.Required,
|
||||
lucide.Asterisk(html.Class("text-error")),
|
||||
lucide.Asterisk(nodx.Class("text-error")),
|
||||
),
|
||||
),
|
||||
gomponents.If(
|
||||
nodx.If(
|
||||
len(params.HelpButtonChildren) > 0,
|
||||
HelpButtonModal(HelpButtonModalParams{
|
||||
ModalTitle: params.Label,
|
||||
@@ -52,33 +50,33 @@ func TextareaControl(params TextareaControlParams) gomponents.Node {
|
||||
}),
|
||||
),
|
||||
),
|
||||
html.Textarea(
|
||||
components.Classes{
|
||||
nodx.Textarea(
|
||||
nodx.ClassMap{
|
||||
"textarea textarea-bordered w-full": true,
|
||||
getTextareaColorClass(params.Color): true,
|
||||
},
|
||||
html.ID(id),
|
||||
html.Name(params.Name),
|
||||
html.Placeholder(params.Placeholder),
|
||||
gomponents.If(
|
||||
nodx.Id(id),
|
||||
nodx.Name(params.Name),
|
||||
nodx.Placeholder(params.Placeholder),
|
||||
nodx.If(
|
||||
params.Required,
|
||||
html.Required(),
|
||||
nodx.Required(""),
|
||||
),
|
||||
gomponents.If(
|
||||
nodx.If(
|
||||
params.AutoComplete != "",
|
||||
html.AutoComplete(params.AutoComplete),
|
||||
nodx.Autocomplete(params.AutoComplete),
|
||||
),
|
||||
gomponents.If(
|
||||
nodx.If(
|
||||
params.Pattern != "",
|
||||
html.Pattern(params.Pattern),
|
||||
nodx.Pattern(params.Pattern),
|
||||
),
|
||||
gomponents.Group(params.Children),
|
||||
nodx.Group(params.Children...),
|
||||
),
|
||||
gomponents.If(
|
||||
nodx.If(
|
||||
params.HelpText != "",
|
||||
html.Label(
|
||||
html.Class("label"),
|
||||
html.For(id),
|
||||
nodx.LabelEl(
|
||||
nodx.Class("label"),
|
||||
nodx.For(id),
|
||||
SpanText(params.HelpText),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
package component
|
||||
|
||||
import (
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
)
|
||||
|
||||
// getTextColorClass returns the text color class for a text that
|
||||
@@ -30,98 +29,98 @@ func getTextColorClass(color color) string {
|
||||
}
|
||||
}
|
||||
|
||||
func H1(children ...gomponents.Node) gomponents.Node {
|
||||
return html.H1(
|
||||
html.Class("text-2xl font-bold desk:text-4xl"),
|
||||
gomponents.Group(children),
|
||||
func H1(children ...nodx.Node) nodx.Node {
|
||||
return nodx.H1(
|
||||
nodx.Class("text-2xl font-bold desk:text-4xl"),
|
||||
nodx.Group(children...),
|
||||
)
|
||||
}
|
||||
|
||||
func H2(children ...gomponents.Node) gomponents.Node {
|
||||
return html.H2(
|
||||
html.Class("text-xl font-bold desk:text-2xl"),
|
||||
gomponents.Group(children),
|
||||
func H2(children ...nodx.Node) nodx.Node {
|
||||
return nodx.H2(
|
||||
nodx.Class("text-xl font-bold desk:text-2xl"),
|
||||
nodx.Group(children...),
|
||||
)
|
||||
}
|
||||
|
||||
func H3(children ...gomponents.Node) gomponents.Node {
|
||||
return html.H3(
|
||||
html.Class("text-lg font-bold desk:text-xl"),
|
||||
gomponents.Group(children),
|
||||
func H3(children ...nodx.Node) nodx.Node {
|
||||
return nodx.H3(
|
||||
nodx.Class("text-lg font-bold desk:text-xl"),
|
||||
nodx.Group(children...),
|
||||
)
|
||||
}
|
||||
|
||||
func H4(children ...gomponents.Node) gomponents.Node {
|
||||
return html.H4(
|
||||
html.Class("text-base font-bold desk:text-lg"),
|
||||
gomponents.Group(children),
|
||||
func H4(children ...nodx.Node) nodx.Node {
|
||||
return nodx.H4(
|
||||
nodx.Class("text-base font-bold desk:text-lg"),
|
||||
nodx.Group(children...),
|
||||
)
|
||||
}
|
||||
|
||||
func H5(children ...gomponents.Node) gomponents.Node {
|
||||
return html.H5(
|
||||
html.Class("text-sm font-bold desk:text-base"),
|
||||
gomponents.Group(children),
|
||||
func H5(children ...nodx.Node) nodx.Node {
|
||||
return nodx.H5(
|
||||
nodx.Class("text-sm font-bold desk:text-base"),
|
||||
nodx.Group(children...),
|
||||
)
|
||||
}
|
||||
|
||||
func H6(children ...gomponents.Node) gomponents.Node {
|
||||
return html.H6(
|
||||
html.Class("text-xs font-bold desk:text-sm"),
|
||||
gomponents.Group(children),
|
||||
func H6(children ...nodx.Node) nodx.Node {
|
||||
return nodx.H6(
|
||||
nodx.Class("text-xs font-bold desk:text-sm"),
|
||||
nodx.Group(children...),
|
||||
)
|
||||
}
|
||||
|
||||
// H1Text is a convenience function to create an H1 element with a
|
||||
// simple text node as its child.
|
||||
func H1Text(text string) gomponents.Node {
|
||||
return H1(gomponents.Text(text))
|
||||
func H1Text(text string) nodx.Node {
|
||||
return H1(nodx.Text(text))
|
||||
}
|
||||
|
||||
// H2Text is a convenience function to create an H2 element with a
|
||||
// simple text node as its child.
|
||||
func H2Text(text string) gomponents.Node {
|
||||
return H2(gomponents.Text(text))
|
||||
func H2Text(text string) nodx.Node {
|
||||
return H2(nodx.Text(text))
|
||||
}
|
||||
|
||||
// H3Text is a convenience function to create an H3 element with a
|
||||
// simple text node as its child.
|
||||
func H3Text(text string) gomponents.Node {
|
||||
return H3(gomponents.Text(text))
|
||||
func H3Text(text string) nodx.Node {
|
||||
return H3(nodx.Text(text))
|
||||
}
|
||||
|
||||
// H4Text is a convenience function to create an H4 element with a
|
||||
// simple text node as its child.
|
||||
func H4Text(text string) gomponents.Node {
|
||||
return H4(gomponents.Text(text))
|
||||
func H4Text(text string) nodx.Node {
|
||||
return H4(nodx.Text(text))
|
||||
}
|
||||
|
||||
// H5Text is a convenience function to create an H5 element with a
|
||||
// simple text node as its child.
|
||||
func H5Text(text string) gomponents.Node {
|
||||
return H5(gomponents.Text(text))
|
||||
func H5Text(text string) nodx.Node {
|
||||
return H5(nodx.Text(text))
|
||||
}
|
||||
|
||||
// H6Text is a convenience function to create an H6 element with a
|
||||
// simple text node as its child.
|
||||
func H6Text(text string) gomponents.Node {
|
||||
return H6(gomponents.Text(text))
|
||||
func H6Text(text string) nodx.Node {
|
||||
return H6(nodx.Text(text))
|
||||
}
|
||||
|
||||
// PText is a convenience function to create a P element with a
|
||||
// simple text node as its child.
|
||||
func PText(text string) gomponents.Node {
|
||||
return html.P(gomponents.Text(text))
|
||||
func PText(text string) nodx.Node {
|
||||
return nodx.P(nodx.Text(text))
|
||||
}
|
||||
|
||||
// SpanText is a convenience function to create a Span element with a
|
||||
// simple text node as its child.
|
||||
func SpanText(text string) gomponents.Node {
|
||||
return html.Span(gomponents.Text(text))
|
||||
func SpanText(text string) nodx.Node {
|
||||
return nodx.SpanEl(nodx.Text(text))
|
||||
}
|
||||
|
||||
// BText is a convenience function to create a B element with a
|
||||
// simple text node as its child.
|
||||
func BText(text string) gomponents.Node {
|
||||
return html.B(gomponents.Text(text))
|
||||
func BText(text string) nodx.Node {
|
||||
return nodx.B(nodx.Text(text))
|
||||
}
|
||||
|
||||
@@ -9,25 +9,24 @@ import (
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/component"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/layout"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
)
|
||||
|
||||
func (h *handlers) indexPageHandler(c echo.Context) error {
|
||||
reqCtx := reqctx.GetCtx(c)
|
||||
return echoutil.RenderGomponent(c, http.StatusOK, indexPage(reqCtx))
|
||||
return echoutil.RenderNodx(c, http.StatusOK, indexPage(reqCtx))
|
||||
}
|
||||
|
||||
func indexPage(reqCtx reqctx.Ctx) gomponents.Node {
|
||||
content := []gomponents.Node{
|
||||
func indexPage(reqCtx reqctx.Ctx) nodx.Node {
|
||||
content := []nodx.Node{
|
||||
component.H1Text("About PG Back Web"),
|
||||
component.H2Text(config.Version),
|
||||
|
||||
html.Div(
|
||||
html.Class("grid grid-cols-2 gap-4 mt-4"),
|
||||
nodx.Div(
|
||||
nodx.Class("grid grid-cols-2 gap-4 mt-4"),
|
||||
|
||||
component.CardBox(component.CardBoxParams{
|
||||
Children: []gomponents.Node{
|
||||
Children: []nodx.Node{
|
||||
component.PText(`
|
||||
PG Back Web was born in July 2024 out of a need for a simple and
|
||||
user-friendly backup solution for self-hosted PostgreSQL databases.
|
||||
@@ -40,38 +39,38 @@ func indexPage(reqCtx reqctx.Ctx) gomponents.Node {
|
||||
}),
|
||||
|
||||
component.CardBox(component.CardBoxParams{
|
||||
Children: []gomponents.Node{
|
||||
html.Table(
|
||||
html.Class("table"),
|
||||
html.Tr(
|
||||
html.Th(component.SpanText("License")),
|
||||
html.Td(
|
||||
html.A(
|
||||
html.Class("link"),
|
||||
html.Href("https://github.com/eduardolat/pgbackweb/blob/main/LICENSE"),
|
||||
html.Target("_blank"),
|
||||
Children: []nodx.Node{
|
||||
nodx.Table(
|
||||
nodx.Class("table"),
|
||||
nodx.Tr(
|
||||
nodx.Th(component.SpanText("License")),
|
||||
nodx.Td(
|
||||
nodx.A(
|
||||
nodx.Class("link"),
|
||||
nodx.Href("https://github.com/eduardolat/pgbackweb/blob/main/LICENSE"),
|
||||
nodx.Target("_blank"),
|
||||
component.SpanText("MIT"),
|
||||
),
|
||||
),
|
||||
),
|
||||
html.Tr(
|
||||
html.Th(component.SpanText("About the author")),
|
||||
html.Td(
|
||||
html.A(
|
||||
html.Class("link"),
|
||||
html.Href("https://eduardo.lat"),
|
||||
html.Target("_blank"),
|
||||
nodx.Tr(
|
||||
nodx.Th(component.SpanText("About the author")),
|
||||
nodx.Td(
|
||||
nodx.A(
|
||||
nodx.Class("link"),
|
||||
nodx.Href("https://eduardo.lat"),
|
||||
nodx.Target("_blank"),
|
||||
component.SpanText("https://eduardo.lat"),
|
||||
),
|
||||
),
|
||||
),
|
||||
html.Tr(
|
||||
html.Th(component.SpanText("Repository")),
|
||||
html.Td(
|
||||
html.A(
|
||||
html.Class("link"),
|
||||
html.Href("https://github.com/eduardolat/pgbackweb"),
|
||||
html.Target("_blank"),
|
||||
nodx.Tr(
|
||||
nodx.Th(component.SpanText("Repository")),
|
||||
nodx.Td(
|
||||
nodx.A(
|
||||
nodx.Class("link"),
|
||||
nodx.Href("https://github.com/eduardolat/pgbackweb"),
|
||||
nodx.Target("_blank"),
|
||||
component.SpanText("https://github.com/eduardolat/pgbackweb"),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -3,15 +3,13 @@ package backups
|
||||
import (
|
||||
"time"
|
||||
|
||||
lucide "github.com/eduardolat/gomponents-lucide"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/component"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/components"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
lucide "github.com/nodxdev/nodxgo-lucide"
|
||||
)
|
||||
|
||||
func localBackupsHelp() []gomponents.Node {
|
||||
return []gomponents.Node{
|
||||
func localBackupsHelp() []nodx.Node {
|
||||
return []nodx.Node{
|
||||
component.H3Text("Local backups"),
|
||||
component.PText(`
|
||||
Local backups are stored in the server where PG Back Web is running.
|
||||
@@ -19,8 +17,8 @@ func localBackupsHelp() []gomponents.Node {
|
||||
volume to this directory to persist the backups in any way you want.
|
||||
`),
|
||||
|
||||
html.Div(
|
||||
html.Class("mt-2"),
|
||||
nodx.Div(
|
||||
nodx.Class("mt-2"),
|
||||
component.H3Text("Remote backups"),
|
||||
component.PText(`
|
||||
Remote backups are stored in a destination. A destination is a remote
|
||||
@@ -31,8 +29,8 @@ func localBackupsHelp() []gomponents.Node {
|
||||
}
|
||||
}
|
||||
|
||||
func cronExpressionHelp() []gomponents.Node {
|
||||
return []gomponents.Node{
|
||||
func cronExpressionHelp() []nodx.Node {
|
||||
return []nodx.Node{
|
||||
component.PText(`
|
||||
A cron expression is a string used to define a schedule for running tasks
|
||||
in Unix-like operating systems. It consists of five fields representing
|
||||
@@ -40,19 +38,19 @@ func cronExpressionHelp() []gomponents.Node {
|
||||
Cron expressions enable precise scheduling of periodic tasks.
|
||||
`),
|
||||
|
||||
html.Div(
|
||||
html.Class("mt-4 flex justify-end items-center space-x-1"),
|
||||
html.A(
|
||||
html.Href("https://en.wikipedia.org/wiki/Cron"),
|
||||
html.Target("_blank"),
|
||||
html.Class("btn btn-ghost"),
|
||||
nodx.Div(
|
||||
nodx.Class("mt-4 flex justify-end items-center space-x-1"),
|
||||
nodx.A(
|
||||
nodx.Href("https://en.wikipedia.org/wiki/Cron"),
|
||||
nodx.Target("_blank"),
|
||||
nodx.Class("btn btn-ghost"),
|
||||
component.SpanText("Learn more"),
|
||||
lucide.ExternalLink(),
|
||||
),
|
||||
html.A(
|
||||
html.Href("https://crontab.guru/examples.html"),
|
||||
html.Target("_blank"),
|
||||
html.Class("btn btn-ghost"),
|
||||
nodx.A(
|
||||
nodx.Href("https://crontab.guru/examples.html"),
|
||||
nodx.Target("_blank"),
|
||||
nodx.Class("btn btn-ghost"),
|
||||
component.SpanText("Examples & common expressions"),
|
||||
lucide.ExternalLink(),
|
||||
),
|
||||
@@ -60,14 +58,14 @@ func cronExpressionHelp() []gomponents.Node {
|
||||
}
|
||||
}
|
||||
|
||||
func timezoneFilenamesHelp() []gomponents.Node {
|
||||
func timezoneFilenamesHelp() []nodx.Node {
|
||||
serverTimezone := time.Now().Location().String()
|
||||
|
||||
return []gomponents.Node{
|
||||
return []nodx.Node{
|
||||
component.PText(`
|
||||
This is the time zone in which the cron expression will be evaluated.
|
||||
`),
|
||||
html.P(
|
||||
nodx.P(
|
||||
component.SpanText(`
|
||||
Backup filenames will always use the server timezone (currently
|
||||
`),
|
||||
@@ -75,12 +73,12 @@ func timezoneFilenamesHelp() []gomponents.Node {
|
||||
component.SpanText(")."),
|
||||
),
|
||||
|
||||
html.Div(
|
||||
html.Class("mt-4 flex justify-end items-center"),
|
||||
html.A(
|
||||
html.Href("https://github.com/eduardolat/pgbackweb?tab=readme-ov-file#configuration"),
|
||||
html.Target("_blank"),
|
||||
html.Class("btn btn-ghost"),
|
||||
nodx.Div(
|
||||
nodx.Class("mt-4 flex justify-end items-center"),
|
||||
nodx.A(
|
||||
nodx.Href("https://github.com/eduardolat/pgbackweb?tab=readme-ov-file#configuration"),
|
||||
nodx.Target("_blank"),
|
||||
nodx.Class("btn btn-ghost"),
|
||||
component.SpanText("Learn more in project README"),
|
||||
lucide.ExternalLink(),
|
||||
),
|
||||
@@ -88,8 +86,8 @@ func timezoneFilenamesHelp() []gomponents.Node {
|
||||
}
|
||||
}
|
||||
|
||||
func destinationDirectoryHelp() []gomponents.Node {
|
||||
return []gomponents.Node{
|
||||
func destinationDirectoryHelp() []nodx.Node {
|
||||
return []nodx.Node{
|
||||
component.PText(`
|
||||
The destination directory is the directory where the backups will be
|
||||
stored. This directory is relative to the base directory of the
|
||||
@@ -97,15 +95,15 @@ func destinationDirectoryHelp() []gomponents.Node {
|
||||
spaces, and should not end with a slash.
|
||||
`),
|
||||
|
||||
html.Div(
|
||||
html.Class("mt-2"),
|
||||
nodx.Div(
|
||||
nodx.Class("mt-2"),
|
||||
component.H3Text("Local backups"),
|
||||
component.PText(`
|
||||
For local backups, the base directory is /backups. So, the backup files
|
||||
will be stored in:
|
||||
`),
|
||||
html.Div(
|
||||
components.Classes{
|
||||
nodx.Div(
|
||||
nodx.ClassMap{
|
||||
"whitespace-nowrap p-1": true,
|
||||
"overflow-x-scroll": true,
|
||||
"font-mono": true,
|
||||
@@ -116,15 +114,15 @@ func destinationDirectoryHelp() []gomponents.Node {
|
||||
),
|
||||
),
|
||||
|
||||
html.Div(
|
||||
html.Class("mt-2"),
|
||||
nodx.Div(
|
||||
nodx.Class("mt-2"),
|
||||
component.H3Text("Remote backups"),
|
||||
component.PText(`
|
||||
For remote backups, the base directory is the root of the bucket. So,
|
||||
the backup files will be stored in:
|
||||
`),
|
||||
html.Div(
|
||||
components.Classes{
|
||||
nodx.Div(
|
||||
nodx.ClassMap{
|
||||
"whitespace-nowrap p-1": true,
|
||||
"overflow-x-scroll": true,
|
||||
"font-mono": true,
|
||||
@@ -137,10 +135,10 @@ func destinationDirectoryHelp() []gomponents.Node {
|
||||
}
|
||||
}
|
||||
|
||||
func retentionDaysHelp() []gomponents.Node {
|
||||
return []gomponents.Node{
|
||||
html.Div(
|
||||
html.Class("space-y-2"),
|
||||
func retentionDaysHelp() []nodx.Node {
|
||||
return []nodx.Node{
|
||||
nodx.Div(
|
||||
nodx.Class("space-y-2"),
|
||||
|
||||
component.PText(`
|
||||
Retention days specifies the number of days to keep backup files before
|
||||
@@ -155,10 +153,10 @@ func retentionDaysHelp() []gomponents.Node {
|
||||
}
|
||||
}
|
||||
|
||||
func pgDumpOptionsHelp() []gomponents.Node {
|
||||
return []gomponents.Node{
|
||||
html.Div(
|
||||
html.Class("space-y-2"),
|
||||
func pgDumpOptionsHelp() []nodx.Node {
|
||||
return []nodx.Node{
|
||||
nodx.Div(
|
||||
nodx.Class("space-y-2"),
|
||||
|
||||
component.PText(`
|
||||
This software uses the battle tested pg_dump utility to create backups. It
|
||||
@@ -170,14 +168,14 @@ func pgDumpOptionsHelp() []gomponents.Node {
|
||||
PG Back Web does not pass any options so the backups are full backups.
|
||||
`),
|
||||
|
||||
html.Div(
|
||||
html.Class("flex justify-end"),
|
||||
html.A(
|
||||
html.Class("btn btn-ghost"),
|
||||
html.Href("https://www.postgresql.org/docs/current/app-pgdump.html"),
|
||||
html.Target("_blank"),
|
||||
nodx.Div(
|
||||
nodx.Class("flex justify-end"),
|
||||
nodx.A(
|
||||
nodx.Class("btn btn-ghost"),
|
||||
nodx.Href("https://www.postgresql.org/docs/current/app-pgdump.html"),
|
||||
nodx.Target("_blank"),
|
||||
component.SpanText("Learn more in pg_dump documentation"),
|
||||
lucide.ExternalLink(html.Class("ml-1")),
|
||||
lucide.ExternalLink(nodx.Class("ml-1")),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
lucide "github.com/eduardolat/gomponents-lucide"
|
||||
"github.com/eduardolat/pgbackweb/internal/database/dbgen"
|
||||
"github.com/eduardolat/pgbackweb/internal/staticdata"
|
||||
"github.com/eduardolat/pgbackweb/internal/util/echoutil"
|
||||
@@ -14,8 +13,8 @@ import (
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/htmx"
|
||||
"github.com/google/uuid"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
lucide "github.com/nodxdev/nodxgo-lucide"
|
||||
)
|
||||
|
||||
func (h *handlers) createBackupHandler(c echo.Context) error {
|
||||
@@ -86,7 +85,7 @@ func (h *handlers) createBackupFormHandler(c echo.Context) error {
|
||||
return htmx.RespondToastError(c, err.Error())
|
||||
}
|
||||
|
||||
return echoutil.RenderGomponent(
|
||||
return echoutil.RenderNodx(
|
||||
c, http.StatusOK, createBackupForm(databases, destinations),
|
||||
)
|
||||
}
|
||||
@@ -94,20 +93,20 @@ func (h *handlers) createBackupFormHandler(c echo.Context) error {
|
||||
func createBackupForm(
|
||||
databases []dbgen.DatabasesServiceGetAllDatabasesRow,
|
||||
destinations []dbgen.DestinationsServiceGetAllDestinationsRow,
|
||||
) gomponents.Node {
|
||||
yesNoOptions := func() gomponents.Node {
|
||||
return gomponents.Group([]gomponents.Node{
|
||||
html.Option(html.Value("true"), gomponents.Text("Yes")),
|
||||
html.Option(html.Value("false"), gomponents.Text("No"), html.Selected()),
|
||||
})
|
||||
) nodx.Node {
|
||||
yesNoOptions := func() nodx.Node {
|
||||
return nodx.Group(
|
||||
nodx.Option(nodx.Value("true"), nodx.Text("Yes")),
|
||||
nodx.Option(nodx.Value("false"), nodx.Text("No"), nodx.Selected("")),
|
||||
)
|
||||
}
|
||||
|
||||
serverTZ := time.Now().Location().String()
|
||||
|
||||
return html.Form(
|
||||
return nodx.FormEl(
|
||||
htmx.HxPost("/dashboard/backups"),
|
||||
htmx.HxDisabledELT("find button"),
|
||||
html.Class("space-y-2 text-base"),
|
||||
nodx.Class("space-y-2 text-base"),
|
||||
|
||||
alpine.XData(`{
|
||||
is_local: "false",
|
||||
@@ -126,11 +125,11 @@ func createBackupForm(
|
||||
Label: "Database",
|
||||
Required: true,
|
||||
Placeholder: "Select a database",
|
||||
Children: []gomponents.Node{
|
||||
component.GMap(
|
||||
Children: []nodx.Node{
|
||||
nodx.Map(
|
||||
databases,
|
||||
func(db dbgen.DatabasesServiceGetAllDatabasesRow) gomponents.Node {
|
||||
return html.Option(html.Value(db.ID.String()), gomponents.Text(db.Name))
|
||||
func(db dbgen.DatabasesServiceGetAllDatabasesRow) nodx.Node {
|
||||
return nodx.Option(nodx.Value(db.ID.String()), nodx.Text(db.Name))
|
||||
},
|
||||
),
|
||||
},
|
||||
@@ -140,10 +139,10 @@ func createBackupForm(
|
||||
Name: "is_local",
|
||||
Label: "Local backup",
|
||||
Required: true,
|
||||
Children: []gomponents.Node{
|
||||
Children: []nodx.Node{
|
||||
alpine.XModel("is_local"),
|
||||
html.Option(html.Value("true"), gomponents.Text("Yes")),
|
||||
html.Option(html.Value("false"), gomponents.Text("No"), html.Selected()),
|
||||
nodx.Option(nodx.Value("true"), nodx.Text("Yes")),
|
||||
nodx.Option(nodx.Value("false"), nodx.Text("No"), nodx.Selected("")),
|
||||
},
|
||||
HelpButtonChildren: localBackupsHelp(),
|
||||
}),
|
||||
@@ -155,11 +154,11 @@ func createBackupForm(
|
||||
Label: "Destination",
|
||||
Required: true,
|
||||
Placeholder: "Select a destination",
|
||||
Children: []gomponents.Node{
|
||||
component.GMap(
|
||||
Children: []nodx.Node{
|
||||
nodx.Map(
|
||||
destinations,
|
||||
func(dest dbgen.DestinationsServiceGetAllDestinationsRow) gomponents.Node {
|
||||
return html.Option(html.Value(dest.ID.String()), gomponents.Text(dest.Name))
|
||||
func(dest dbgen.DestinationsServiceGetAllDestinationsRow) nodx.Node {
|
||||
return nodx.Option(nodx.Value(dest.ID.String()), nodx.Text(dest.Name))
|
||||
},
|
||||
),
|
||||
},
|
||||
@@ -182,16 +181,16 @@ func createBackupForm(
|
||||
Label: "Time zone",
|
||||
Required: true,
|
||||
Placeholder: "Select a time zone",
|
||||
Children: []gomponents.Node{
|
||||
component.GMap(
|
||||
Children: []nodx.Node{
|
||||
nodx.Map(
|
||||
staticdata.Timezones,
|
||||
func(tz staticdata.Timezone) gomponents.Node {
|
||||
var selected gomponents.Node
|
||||
func(tz staticdata.Timezone) nodx.Node {
|
||||
var selected nodx.Node
|
||||
if tz.TzCode == serverTZ {
|
||||
selected = html.Selected()
|
||||
selected = nodx.Selected("")
|
||||
}
|
||||
|
||||
return html.Option(html.Value(tz.TzCode), gomponents.Text(tz.Label), selected)
|
||||
return nodx.Option(nodx.Value(tz.TzCode), nodx.Text(tz.Label), selected)
|
||||
},
|
||||
),
|
||||
},
|
||||
@@ -217,9 +216,9 @@ func createBackupForm(
|
||||
Type: component.InputTypeNumber,
|
||||
Pattern: "[0-9]+",
|
||||
HelpButtonChildren: retentionDaysHelp(),
|
||||
Children: []gomponents.Node{
|
||||
html.Min("0"),
|
||||
html.Max("36500"),
|
||||
Children: []nodx.Node{
|
||||
nodx.Min("0"),
|
||||
nodx.Max("36500"),
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -227,16 +226,16 @@ func createBackupForm(
|
||||
Name: "is_active",
|
||||
Label: "Activate backup",
|
||||
Required: true,
|
||||
Children: []gomponents.Node{
|
||||
html.Option(html.Value("true"), gomponents.Text("Yes")),
|
||||
html.Option(html.Value("false"), gomponents.Text("No")),
|
||||
Children: []nodx.Node{
|
||||
nodx.Option(nodx.Value("true"), nodx.Text("Yes")),
|
||||
nodx.Option(nodx.Value("false"), nodx.Text("No")),
|
||||
},
|
||||
}),
|
||||
|
||||
html.Div(
|
||||
html.Class("pt-4"),
|
||||
html.Div(
|
||||
html.Class("flex justify-start items-center space-x-1"),
|
||||
nodx.Div(
|
||||
nodx.Class("pt-4"),
|
||||
nodx.Div(
|
||||
nodx.Class("flex justify-start items-center space-x-1"),
|
||||
component.H2Text("Options"),
|
||||
component.HelpButtonModal(component.HelpButtonModalParams{
|
||||
ModalTitle: "Backup options",
|
||||
@@ -244,14 +243,14 @@ func createBackupForm(
|
||||
}),
|
||||
),
|
||||
|
||||
html.Div(
|
||||
html.Class("mt-2 grid grid-cols-2 gap-2"),
|
||||
nodx.Div(
|
||||
nodx.Class("mt-2 grid grid-cols-2 gap-2"),
|
||||
|
||||
component.SelectControl(component.SelectControlParams{
|
||||
Name: "opt_data_only",
|
||||
Label: "--data-only",
|
||||
Required: true,
|
||||
Children: []gomponents.Node{
|
||||
Children: []nodx.Node{
|
||||
yesNoOptions(),
|
||||
},
|
||||
}),
|
||||
@@ -260,7 +259,7 @@ func createBackupForm(
|
||||
Name: "opt_schema_only",
|
||||
Label: "--schema-only",
|
||||
Required: true,
|
||||
Children: []gomponents.Node{
|
||||
Children: []nodx.Node{
|
||||
yesNoOptions(),
|
||||
},
|
||||
}),
|
||||
@@ -269,7 +268,7 @@ func createBackupForm(
|
||||
Name: "opt_clean",
|
||||
Label: "--clean",
|
||||
Required: true,
|
||||
Children: []gomponents.Node{
|
||||
Children: []nodx.Node{
|
||||
yesNoOptions(),
|
||||
},
|
||||
}),
|
||||
@@ -278,7 +277,7 @@ func createBackupForm(
|
||||
Name: "opt_if_exists",
|
||||
Label: "--if-exists",
|
||||
Required: true,
|
||||
Children: []gomponents.Node{
|
||||
Children: []nodx.Node{
|
||||
yesNoOptions(),
|
||||
},
|
||||
}),
|
||||
@@ -287,7 +286,7 @@ func createBackupForm(
|
||||
Name: "opt_create",
|
||||
Label: "--create",
|
||||
Required: true,
|
||||
Children: []gomponents.Node{
|
||||
Children: []nodx.Node{
|
||||
yesNoOptions(),
|
||||
},
|
||||
}),
|
||||
@@ -296,19 +295,19 @@ func createBackupForm(
|
||||
Name: "opt_no_comments",
|
||||
Label: "--no-comments",
|
||||
Required: true,
|
||||
Children: []gomponents.Node{
|
||||
Children: []nodx.Node{
|
||||
yesNoOptions(),
|
||||
},
|
||||
}),
|
||||
),
|
||||
),
|
||||
|
||||
html.Div(
|
||||
html.Class("flex justify-end items-center space-x-2 pt-2"),
|
||||
nodx.Div(
|
||||
nodx.Class("flex justify-end items-center space-x-2 pt-2"),
|
||||
component.HxLoadingMd(),
|
||||
html.Button(
|
||||
html.Class("btn btn-primary"),
|
||||
html.Type("submit"),
|
||||
nodx.Button(
|
||||
nodx.Class("btn btn-primary"),
|
||||
nodx.Type("submit"),
|
||||
component.SpanText("Save"),
|
||||
lucide.Save(),
|
||||
),
|
||||
@@ -316,30 +315,30 @@ func createBackupForm(
|
||||
)
|
||||
}
|
||||
|
||||
func createBackupButton() gomponents.Node {
|
||||
func createBackupButton() nodx.Node {
|
||||
mo := component.Modal(component.ModalParams{
|
||||
Size: component.SizeLg,
|
||||
Title: "Create backup",
|
||||
Content: []gomponents.Node{
|
||||
html.Div(
|
||||
Content: []nodx.Node{
|
||||
nodx.Div(
|
||||
htmx.HxGet("/dashboard/backups/create-form"),
|
||||
htmx.HxSwap("outerHTML"),
|
||||
htmx.HxTrigger("intersect once"),
|
||||
html.Class("p-10 flex justify-center"),
|
||||
nodx.Class("p-10 flex justify-center"),
|
||||
component.HxLoadingMd(),
|
||||
),
|
||||
},
|
||||
})
|
||||
|
||||
button := html.Button(
|
||||
button := nodx.Button(
|
||||
mo.OpenerAttr,
|
||||
html.Class("btn btn-primary"),
|
||||
nodx.Class("btn btn-primary"),
|
||||
component.SpanText("Create backup"),
|
||||
lucide.Plus(),
|
||||
)
|
||||
|
||||
return html.Div(
|
||||
html.Class("inline-block"),
|
||||
return nodx.Div(
|
||||
nodx.Class("inline-block"),
|
||||
mo.HTML,
|
||||
button,
|
||||
)
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package backups
|
||||
|
||||
import (
|
||||
lucide "github.com/eduardolat/gomponents-lucide"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/component"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/htmx"
|
||||
"github.com/google/uuid"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/maragudk/gomponents"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
lucide "github.com/nodxdev/nodxgo-lucide"
|
||||
)
|
||||
|
||||
func (h *handlers) deleteBackupHandler(c echo.Context) error {
|
||||
@@ -24,7 +24,7 @@ func (h *handlers) deleteBackupHandler(c echo.Context) error {
|
||||
return htmx.RespondRefresh(c)
|
||||
}
|
||||
|
||||
func deleteBackupButton(backupID uuid.UUID) gomponents.Node {
|
||||
func deleteBackupButton(backupID uuid.UUID) nodx.Node {
|
||||
return component.OptionsDropdownButton(
|
||||
htmx.HxDelete("/dashboard/backups/"+backupID.String()),
|
||||
htmx.HxConfirm("Are you sure you want to delete this backup?"),
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package backups
|
||||
|
||||
import (
|
||||
lucide "github.com/eduardolat/gomponents-lucide"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/component"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/htmx"
|
||||
"github.com/google/uuid"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/maragudk/gomponents"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
lucide "github.com/nodxdev/nodxgo-lucide"
|
||||
)
|
||||
|
||||
func (h *handlers) duplicateBackupHandler(c echo.Context) error {
|
||||
@@ -24,7 +24,7 @@ func (h *handlers) duplicateBackupHandler(c echo.Context) error {
|
||||
return htmx.RespondRefresh(c)
|
||||
}
|
||||
|
||||
func duplicateBackupButton(backupID uuid.UUID) gomponents.Node {
|
||||
func duplicateBackupButton(backupID uuid.UUID) nodx.Node {
|
||||
return component.OptionsDropdownButton(
|
||||
htmx.HxPost("/dashboard/backups/"+backupID.String()+"/duplicate"),
|
||||
htmx.HxConfirm("Are you sure you want to duplicate this backup?"),
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
|
||||
lucide "github.com/eduardolat/gomponents-lucide"
|
||||
"github.com/eduardolat/pgbackweb/internal/database/dbgen"
|
||||
"github.com/eduardolat/pgbackweb/internal/staticdata"
|
||||
"github.com/eduardolat/pgbackweb/internal/validate"
|
||||
@@ -12,8 +11,8 @@ import (
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/htmx"
|
||||
"github.com/google/uuid"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
lucide "github.com/nodxdev/nodxgo-lucide"
|
||||
)
|
||||
|
||||
func (h *handlers) editBackupHandler(c echo.Context) error {
|
||||
@@ -69,30 +68,30 @@ func (h *handlers) editBackupHandler(c echo.Context) error {
|
||||
return htmx.RespondAlertWithRefresh(c, "Backup updated")
|
||||
}
|
||||
|
||||
func editBackupButton(backup dbgen.BackupsServicePaginateBackupsRow) gomponents.Node {
|
||||
yesNoOptions := func(value bool) gomponents.Node {
|
||||
return gomponents.Group([]gomponents.Node{
|
||||
html.Option(
|
||||
html.Value("true"),
|
||||
gomponents.Text("Yes"),
|
||||
gomponents.If(value, html.Selected()),
|
||||
func editBackupButton(backup dbgen.BackupsServicePaginateBackupsRow) nodx.Node {
|
||||
yesNoOptions := func(value bool) nodx.Node {
|
||||
return nodx.Group(
|
||||
nodx.Option(
|
||||
nodx.Value("true"),
|
||||
nodx.Text("Yes"),
|
||||
nodx.If(value, nodx.Selected("")),
|
||||
),
|
||||
html.Option(
|
||||
html.Value("false"),
|
||||
gomponents.Text("No"),
|
||||
gomponents.If(!value, html.Selected()),
|
||||
nodx.Option(
|
||||
nodx.Value("false"),
|
||||
nodx.Text("No"),
|
||||
nodx.If(!value, nodx.Selected("")),
|
||||
),
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
mo := component.Modal(component.ModalParams{
|
||||
Size: component.SizeLg,
|
||||
Title: "Edit backup",
|
||||
Content: []gomponents.Node{
|
||||
html.Form(
|
||||
Content: []nodx.Node{
|
||||
nodx.FormEl(
|
||||
htmx.HxPost("/dashboard/backups/"+backup.ID.String()+"/edit"),
|
||||
htmx.HxDisabledELT("find button"),
|
||||
html.Class("space-y-2 text-base"),
|
||||
nodx.Class("space-y-2 text-base"),
|
||||
|
||||
component.InputControl(component.InputControlParams{
|
||||
Name: "name",
|
||||
@@ -100,8 +99,8 @@ func editBackupButton(backup dbgen.BackupsServicePaginateBackupsRow) gomponents.
|
||||
Placeholder: "My backup",
|
||||
Required: true,
|
||||
Type: component.InputTypeText,
|
||||
Children: []gomponents.Node{
|
||||
html.Value(backup.Name),
|
||||
Children: []nodx.Node{
|
||||
nodx.Value(backup.Name),
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -113,8 +112,8 @@ func editBackupButton(backup dbgen.BackupsServicePaginateBackupsRow) gomponents.
|
||||
Type: component.InputTypeText,
|
||||
HelpText: "The cron expression to schedule the backup",
|
||||
Pattern: `^\S+\s+\S+\s+\S+\s+\S+\s+\S+$`,
|
||||
Children: []gomponents.Node{
|
||||
html.Value(backup.CronExpression),
|
||||
Children: []nodx.Node{
|
||||
nodx.Value(backup.CronExpression),
|
||||
},
|
||||
HelpButtonChildren: cronExpressionHelp(),
|
||||
}),
|
||||
@@ -124,16 +123,16 @@ func editBackupButton(backup dbgen.BackupsServicePaginateBackupsRow) gomponents.
|
||||
Label: "Time zone",
|
||||
Required: true,
|
||||
Placeholder: "Select a time zone",
|
||||
Children: []gomponents.Node{
|
||||
component.GMap(
|
||||
Children: []nodx.Node{
|
||||
nodx.Map(
|
||||
staticdata.Timezones,
|
||||
func(tz staticdata.Timezone) gomponents.Node {
|
||||
return html.Option(
|
||||
html.Value(tz.TzCode),
|
||||
gomponents.Text(tz.Label),
|
||||
gomponents.If(
|
||||
func(tz staticdata.Timezone) nodx.Node {
|
||||
return nodx.Option(
|
||||
nodx.Value(tz.TzCode),
|
||||
nodx.Text(tz.Label),
|
||||
nodx.If(
|
||||
tz.TzCode == backup.TimeZone,
|
||||
html.Selected(),
|
||||
nodx.Selected(""),
|
||||
),
|
||||
)
|
||||
},
|
||||
@@ -151,8 +150,8 @@ func editBackupButton(backup dbgen.BackupsServicePaginateBackupsRow) gomponents.
|
||||
HelpText: "Relative to the base directory of the destination",
|
||||
HelpButtonChildren: destinationDirectoryHelp(),
|
||||
Pattern: `^\/\S*[^\/]$`,
|
||||
Children: []gomponents.Node{
|
||||
html.Value(backup.DestDir),
|
||||
Children: []nodx.Node{
|
||||
nodx.Value(backup.DestDir),
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -164,10 +163,10 @@ func editBackupButton(backup dbgen.BackupsServicePaginateBackupsRow) gomponents.
|
||||
Type: component.InputTypeNumber,
|
||||
Pattern: "[0-9]+",
|
||||
HelpButtonChildren: retentionDaysHelp(),
|
||||
Children: []gomponents.Node{
|
||||
html.Min("0"),
|
||||
html.Max("36500"),
|
||||
html.Value(fmt.Sprintf("%d", backup.RetentionDays)),
|
||||
Children: []nodx.Node{
|
||||
nodx.Min("0"),
|
||||
nodx.Max("36500"),
|
||||
nodx.Value(fmt.Sprintf("%d", backup.RetentionDays)),
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -175,15 +174,15 @@ func editBackupButton(backup dbgen.BackupsServicePaginateBackupsRow) gomponents.
|
||||
Name: "is_active",
|
||||
Label: "Activate backup",
|
||||
Required: true,
|
||||
Children: []gomponents.Node{
|
||||
Children: []nodx.Node{
|
||||
yesNoOptions(backup.IsActive),
|
||||
},
|
||||
}),
|
||||
|
||||
html.Div(
|
||||
html.Class("pt-4"),
|
||||
html.Div(
|
||||
html.Class("flex justify-start items-center space-x-1"),
|
||||
nodx.Div(
|
||||
nodx.Class("pt-4"),
|
||||
nodx.Div(
|
||||
nodx.Class("flex justify-start items-center space-x-1"),
|
||||
component.H2Text("Options"),
|
||||
component.HelpButtonModal(component.HelpButtonModalParams{
|
||||
ModalTitle: "Backup options",
|
||||
@@ -191,13 +190,13 @@ func editBackupButton(backup dbgen.BackupsServicePaginateBackupsRow) gomponents.
|
||||
}),
|
||||
),
|
||||
|
||||
html.Div(
|
||||
html.Class("mt-2 grid grid-cols-2 gap-2"),
|
||||
nodx.Div(
|
||||
nodx.Class("mt-2 grid grid-cols-2 gap-2"),
|
||||
component.SelectControl(component.SelectControlParams{
|
||||
Name: "opt_data_only",
|
||||
Label: "--data-only",
|
||||
Required: true,
|
||||
Children: []gomponents.Node{
|
||||
Children: []nodx.Node{
|
||||
yesNoOptions(backup.OptDataOnly),
|
||||
},
|
||||
}),
|
||||
@@ -206,7 +205,7 @@ func editBackupButton(backup dbgen.BackupsServicePaginateBackupsRow) gomponents.
|
||||
Name: "opt_schema_only",
|
||||
Label: "--schema-only",
|
||||
Required: true,
|
||||
Children: []gomponents.Node{
|
||||
Children: []nodx.Node{
|
||||
yesNoOptions(backup.OptSchemaOnly),
|
||||
},
|
||||
}),
|
||||
@@ -215,7 +214,7 @@ func editBackupButton(backup dbgen.BackupsServicePaginateBackupsRow) gomponents.
|
||||
Name: "opt_clean",
|
||||
Label: "--clean",
|
||||
Required: true,
|
||||
Children: []gomponents.Node{
|
||||
Children: []nodx.Node{
|
||||
yesNoOptions(backup.OptClean),
|
||||
},
|
||||
}),
|
||||
@@ -224,7 +223,7 @@ func editBackupButton(backup dbgen.BackupsServicePaginateBackupsRow) gomponents.
|
||||
Name: "opt_if_exists",
|
||||
Label: "--if-exists",
|
||||
Required: true,
|
||||
Children: []gomponents.Node{
|
||||
Children: []nodx.Node{
|
||||
yesNoOptions(backup.OptIfExists),
|
||||
},
|
||||
}),
|
||||
@@ -233,7 +232,7 @@ func editBackupButton(backup dbgen.BackupsServicePaginateBackupsRow) gomponents.
|
||||
Name: "opt_create",
|
||||
Label: "--create",
|
||||
Required: true,
|
||||
Children: []gomponents.Node{
|
||||
Children: []nodx.Node{
|
||||
yesNoOptions(backup.OptCreate),
|
||||
},
|
||||
}),
|
||||
@@ -242,19 +241,19 @@ func editBackupButton(backup dbgen.BackupsServicePaginateBackupsRow) gomponents.
|
||||
Name: "opt_no_comments",
|
||||
Label: "--no-comments",
|
||||
Required: true,
|
||||
Children: []gomponents.Node{
|
||||
Children: []nodx.Node{
|
||||
yesNoOptions(backup.OptNoComments),
|
||||
},
|
||||
}),
|
||||
),
|
||||
),
|
||||
|
||||
html.Div(
|
||||
html.Class("flex justify-end items-center space-x-2 pt-2"),
|
||||
nodx.Div(
|
||||
nodx.Class("flex justify-end items-center space-x-2 pt-2"),
|
||||
component.HxLoadingMd(),
|
||||
html.Button(
|
||||
html.Class("btn btn-primary"),
|
||||
html.Type("submit"),
|
||||
nodx.Button(
|
||||
nodx.Class("btn btn-primary"),
|
||||
nodx.Type("submit"),
|
||||
component.SpanText("Save"),
|
||||
lucide.Save(),
|
||||
),
|
||||
@@ -263,7 +262,7 @@ func editBackupButton(backup dbgen.BackupsServicePaginateBackupsRow) gomponents.
|
||||
},
|
||||
})
|
||||
|
||||
return html.Div(
|
||||
return nodx.Div(
|
||||
mo.HTML,
|
||||
component.OptionsDropdownButton(
|
||||
mo.OpenerAttr,
|
||||
|
||||
@@ -9,47 +9,46 @@ import (
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/htmx"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/layout"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
)
|
||||
|
||||
func (h *handlers) indexPageHandler(c echo.Context) error {
|
||||
reqCtx := reqctx.GetCtx(c)
|
||||
return echoutil.RenderGomponent(c, http.StatusOK, indexPage(reqCtx))
|
||||
return echoutil.RenderNodx(c, http.StatusOK, indexPage(reqCtx))
|
||||
}
|
||||
|
||||
func indexPage(reqCtx reqctx.Ctx) gomponents.Node {
|
||||
content := []gomponents.Node{
|
||||
html.Div(
|
||||
html.Class("flex justify-between items-start"),
|
||||
func indexPage(reqCtx reqctx.Ctx) nodx.Node {
|
||||
content := []nodx.Node{
|
||||
nodx.Div(
|
||||
nodx.Class("flex justify-between items-start"),
|
||||
component.H1Text("Backups"),
|
||||
createBackupButton(),
|
||||
),
|
||||
component.CardBox(component.CardBoxParams{
|
||||
Class: "mt-4",
|
||||
Children: []gomponents.Node{
|
||||
html.Div(
|
||||
html.Class("overflow-x-auto"),
|
||||
html.Table(
|
||||
html.Class("table text-nowrap"),
|
||||
html.THead(
|
||||
html.Tr(
|
||||
html.Th(html.Class("w-1")),
|
||||
html.Th(component.SpanText("Name")),
|
||||
html.Th(component.SpanText("Database")),
|
||||
html.Th(component.SpanText("Destination")),
|
||||
html.Th(component.SpanText("Schedule")),
|
||||
html.Th(component.SpanText("Retention")),
|
||||
html.Th(component.SpanText("--data-only")),
|
||||
html.Th(component.SpanText("--schema-only")),
|
||||
html.Th(component.SpanText("--clean")),
|
||||
html.Th(component.SpanText("--if-exists")),
|
||||
html.Th(component.SpanText("--create")),
|
||||
html.Th(component.SpanText("--no-comments")),
|
||||
html.Th(component.SpanText("Created at")),
|
||||
Children: []nodx.Node{
|
||||
nodx.Div(
|
||||
nodx.Class("overflow-x-auto"),
|
||||
nodx.Table(
|
||||
nodx.Class("table text-nowrap"),
|
||||
nodx.Thead(
|
||||
nodx.Tr(
|
||||
nodx.Th(nodx.Class("w-1")),
|
||||
nodx.Th(component.SpanText("Name")),
|
||||
nodx.Th(component.SpanText("Database")),
|
||||
nodx.Th(component.SpanText("Destination")),
|
||||
nodx.Th(component.SpanText("Schedule")),
|
||||
nodx.Th(component.SpanText("Retention")),
|
||||
nodx.Th(component.SpanText("--data-only")),
|
||||
nodx.Th(component.SpanText("--schema-only")),
|
||||
nodx.Th(component.SpanText("--clean")),
|
||||
nodx.Th(component.SpanText("--if-exists")),
|
||||
nodx.Th(component.SpanText("--create")),
|
||||
nodx.Th(component.SpanText("--no-comments")),
|
||||
nodx.Th(component.SpanText("Created at")),
|
||||
),
|
||||
),
|
||||
html.TBody(
|
||||
nodx.Tbody(
|
||||
component.SkeletonTr(8),
|
||||
htmx.HxGet("/dashboard/backups/list?page=1"),
|
||||
htmx.HxTrigger("load"),
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
lucide "github.com/eduardolat/gomponents-lucide"
|
||||
"github.com/eduardolat/pgbackweb/internal/database/dbgen"
|
||||
"github.com/eduardolat/pgbackweb/internal/service/backups"
|
||||
"github.com/eduardolat/pgbackweb/internal/util/echoutil"
|
||||
@@ -14,8 +13,8 @@ import (
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/component"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/htmx"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
lucide "github.com/nodxdev/nodxgo-lucide"
|
||||
)
|
||||
|
||||
func (h *handlers) listBackupsHandler(c echo.Context) error {
|
||||
@@ -41,7 +40,7 @@ func (h *handlers) listBackupsHandler(c echo.Context) error {
|
||||
return htmx.RespondToastError(c, err.Error())
|
||||
}
|
||||
|
||||
return echoutil.RenderGomponent(
|
||||
return echoutil.RenderNodx(
|
||||
c, http.StatusOK, listBackups(pagination, backups),
|
||||
)
|
||||
}
|
||||
@@ -49,7 +48,7 @@ func (h *handlers) listBackupsHandler(c echo.Context) error {
|
||||
func listBackups(
|
||||
pagination paginateutil.PaginateResponse,
|
||||
backups []dbgen.BackupsServicePaginateBackupsRow,
|
||||
) gomponents.Node {
|
||||
) nodx.Node {
|
||||
if len(backups) < 1 {
|
||||
return component.EmptyResultsTr(component.EmptyResultsParams{
|
||||
Title: "No backups found",
|
||||
@@ -57,23 +56,23 @@ func listBackups(
|
||||
})
|
||||
}
|
||||
|
||||
yesNoSpan := func(b bool) gomponents.Node {
|
||||
yesNoSpan := func(b bool) nodx.Node {
|
||||
if b {
|
||||
return component.SpanText("Yes")
|
||||
}
|
||||
return component.SpanText("No")
|
||||
}
|
||||
|
||||
trs := []gomponents.Node{}
|
||||
trs := []nodx.Node{}
|
||||
for _, backup := range backups {
|
||||
trs = append(trs, html.Tr(
|
||||
html.Td(component.OptionsDropdown(
|
||||
trs = append(trs, nodx.Tr(
|
||||
nodx.Td(component.OptionsDropdown(
|
||||
component.OptionsDropdownA(
|
||||
html.Class("btn btn-sm btn-ghost btn-square"),
|
||||
html.Href(
|
||||
nodx.Class("btn btn-sm btn-ghost btn-square"),
|
||||
nodx.Href(
|
||||
fmt.Sprintf("/dashboard/executions?backup=%s", backup.ID),
|
||||
),
|
||||
html.Target("_blank"),
|
||||
nodx.Target("_blank"),
|
||||
lucide.List(),
|
||||
component.SpanText("Show executions"),
|
||||
),
|
||||
@@ -82,49 +81,49 @@ func listBackups(
|
||||
duplicateBackupButton(backup.ID),
|
||||
deleteBackupButton(backup.ID),
|
||||
)),
|
||||
html.Td(
|
||||
html.Div(
|
||||
html.Class("flex items-center space-x-2"),
|
||||
nodx.Td(
|
||||
nodx.Div(
|
||||
nodx.Class("flex items-center space-x-2"),
|
||||
component.IsActivePing(backup.IsActive),
|
||||
component.SpanText(backup.Name),
|
||||
),
|
||||
),
|
||||
html.Td(component.SpanText(backup.DatabaseName)),
|
||||
html.Td(component.PrettyDestinationName(
|
||||
nodx.Td(component.SpanText(backup.DatabaseName)),
|
||||
nodx.Td(component.PrettyDestinationName(
|
||||
backup.IsLocal, backup.DestinationName,
|
||||
)),
|
||||
html.Td(
|
||||
html.Class("font-mono"),
|
||||
html.Div(
|
||||
html.Class("flex flex-col items-start text-xs"),
|
||||
nodx.Td(
|
||||
nodx.Class("font-mono"),
|
||||
nodx.Div(
|
||||
nodx.Class("flex flex-col items-start text-xs"),
|
||||
component.SpanText(backup.CronExpression),
|
||||
component.SpanText(backup.TimeZone),
|
||||
),
|
||||
),
|
||||
html.Td(
|
||||
gomponents.If(
|
||||
nodx.Td(
|
||||
nodx.If(
|
||||
backup.RetentionDays == 0,
|
||||
lucide.Infinity(),
|
||||
),
|
||||
gomponents.If(
|
||||
nodx.If(
|
||||
backup.RetentionDays > 0,
|
||||
component.SpanText(fmt.Sprintf("%d days", backup.RetentionDays)),
|
||||
),
|
||||
),
|
||||
html.Td(yesNoSpan(backup.OptDataOnly)),
|
||||
html.Td(yesNoSpan(backup.OptSchemaOnly)),
|
||||
html.Td(yesNoSpan(backup.OptClean)),
|
||||
html.Td(yesNoSpan(backup.OptIfExists)),
|
||||
html.Td(yesNoSpan(backup.OptCreate)),
|
||||
html.Td(yesNoSpan(backup.OptNoComments)),
|
||||
html.Td(component.SpanText(
|
||||
nodx.Td(yesNoSpan(backup.OptDataOnly)),
|
||||
nodx.Td(yesNoSpan(backup.OptSchemaOnly)),
|
||||
nodx.Td(yesNoSpan(backup.OptClean)),
|
||||
nodx.Td(yesNoSpan(backup.OptIfExists)),
|
||||
nodx.Td(yesNoSpan(backup.OptCreate)),
|
||||
nodx.Td(yesNoSpan(backup.OptNoComments)),
|
||||
nodx.Td(component.SpanText(
|
||||
backup.CreatedAt.Local().Format(timeutil.LayoutYYYYMMDDHHMMSSPretty),
|
||||
)),
|
||||
))
|
||||
}
|
||||
|
||||
if pagination.HasNextPage {
|
||||
trs = append(trs, html.Tr(
|
||||
trs = append(trs, nodx.Tr(
|
||||
htmx.HxGet(fmt.Sprintf(
|
||||
"/dashboard/backups/list?page=%d", pagination.NextPage,
|
||||
)),
|
||||
|
||||
@@ -3,12 +3,12 @@ package backups
|
||||
import (
|
||||
"context"
|
||||
|
||||
lucide "github.com/eduardolat/gomponents-lucide"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/component"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/htmx"
|
||||
"github.com/google/uuid"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/maragudk/gomponents"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
lucide "github.com/nodxdev/nodxgo-lucide"
|
||||
)
|
||||
|
||||
func (h *handlers) manualRunHandler(c echo.Context) error {
|
||||
@@ -24,7 +24,7 @@ func (h *handlers) manualRunHandler(c echo.Context) error {
|
||||
return htmx.RespondToastSuccess(c, "Backup started, check the backup executions for more details")
|
||||
}
|
||||
|
||||
func manualRunbutton(backupID uuid.UUID) gomponents.Node {
|
||||
func manualRunbutton(backupID uuid.UUID) nodx.Node {
|
||||
return component.OptionsDropdownButton(
|
||||
htmx.HxPost("/dashboard/backups/"+backupID.String()+"/run"),
|
||||
htmx.HxDisabledELT("this"),
|
||||
|
||||
@@ -3,14 +3,13 @@ package databases
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
lucide "github.com/eduardolat/gomponents-lucide"
|
||||
"github.com/eduardolat/pgbackweb/internal/database/dbgen"
|
||||
"github.com/eduardolat/pgbackweb/internal/validate"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/component"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/htmx"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
lucide "github.com/nodxdev/nodxgo-lucide"
|
||||
)
|
||||
|
||||
type createDatabaseDTO struct {
|
||||
@@ -44,24 +43,24 @@ func (h *handlers) createDatabaseHandler(c echo.Context) error {
|
||||
return htmx.RespondRedirect(c, "/dashboard/databases")
|
||||
}
|
||||
|
||||
func createDatabaseButton() gomponents.Node {
|
||||
htmxAttributes := func(url string) gomponents.Node {
|
||||
return gomponents.Group([]gomponents.Node{
|
||||
func createDatabaseButton() nodx.Node {
|
||||
htmxAttributes := func(url string) nodx.Node {
|
||||
return nodx.Group(
|
||||
htmx.HxPost(url),
|
||||
htmx.HxInclude("#create-database-form"),
|
||||
htmx.HxDisabledELT(".create-database-btn"),
|
||||
htmx.HxIndicator("#create-database-loading"),
|
||||
htmx.HxValidate("true"),
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
mo := component.Modal(component.ModalParams{
|
||||
Size: component.SizeMd,
|
||||
Title: "Create database",
|
||||
Content: []gomponents.Node{
|
||||
html.Form(
|
||||
html.ID("create-database-form"),
|
||||
html.Class("space-y-2"),
|
||||
Content: []nodx.Node{
|
||||
nodx.FormEl(
|
||||
nodx.Id("create-database-form"),
|
||||
nodx.Class("space-y-2"),
|
||||
|
||||
component.InputControl(component.InputControlParams{
|
||||
Name: "name",
|
||||
@@ -78,7 +77,7 @@ func createDatabaseButton() gomponents.Node {
|
||||
Placeholder: "Select a version",
|
||||
Required: true,
|
||||
HelpText: "The version of the database",
|
||||
Children: []gomponents.Node{
|
||||
Children: []nodx.Node{
|
||||
component.PGVersionSelectOptions(sql.NullString{}),
|
||||
},
|
||||
}),
|
||||
@@ -93,24 +92,24 @@ func createDatabaseButton() gomponents.Node {
|
||||
}),
|
||||
),
|
||||
|
||||
html.Div(
|
||||
html.Class("flex justify-between items-center pt-4"),
|
||||
html.Div(
|
||||
html.Button(
|
||||
nodx.Div(
|
||||
nodx.Class("flex justify-between items-center pt-4"),
|
||||
nodx.Div(
|
||||
nodx.Button(
|
||||
htmxAttributes("/dashboard/databases/test"),
|
||||
html.Class("create-database-btn btn btn-neutral btn-outline"),
|
||||
html.Type("button"),
|
||||
nodx.Class("create-database-btn btn btn-neutral btn-outline"),
|
||||
nodx.Type("button"),
|
||||
component.SpanText("Test connection"),
|
||||
lucide.DatabaseZap(),
|
||||
),
|
||||
),
|
||||
html.Div(
|
||||
html.Class("flex justify-end items-center space-x-2"),
|
||||
nodx.Div(
|
||||
nodx.Class("flex justify-end items-center space-x-2"),
|
||||
component.HxLoadingMd("create-database-loading"),
|
||||
html.Button(
|
||||
nodx.Button(
|
||||
htmxAttributes("/dashboard/databases"),
|
||||
html.Class("create-database-btn btn btn-primary"),
|
||||
html.Type("button"),
|
||||
nodx.Class("create-database-btn btn btn-primary"),
|
||||
nodx.Type("button"),
|
||||
component.SpanText("Save"),
|
||||
lucide.Save(),
|
||||
),
|
||||
@@ -119,15 +118,15 @@ func createDatabaseButton() gomponents.Node {
|
||||
},
|
||||
})
|
||||
|
||||
button := html.Button(
|
||||
button := nodx.Button(
|
||||
mo.OpenerAttr,
|
||||
html.Class("btn btn-primary"),
|
||||
nodx.Class("btn btn-primary"),
|
||||
component.SpanText("Create database"),
|
||||
lucide.Plus(),
|
||||
)
|
||||
|
||||
return html.Div(
|
||||
html.Class("inline-block"),
|
||||
return nodx.Div(
|
||||
nodx.Class("inline-block"),
|
||||
mo.HTML,
|
||||
button,
|
||||
)
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package databases
|
||||
|
||||
import (
|
||||
lucide "github.com/eduardolat/gomponents-lucide"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/component"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/htmx"
|
||||
"github.com/google/uuid"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/maragudk/gomponents"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
lucide "github.com/nodxdev/nodxgo-lucide"
|
||||
)
|
||||
|
||||
func (h *handlers) deleteDatabaseHandler(c echo.Context) error {
|
||||
@@ -24,7 +24,7 @@ func (h *handlers) deleteDatabaseHandler(c echo.Context) error {
|
||||
return htmx.RespondRefresh(c)
|
||||
}
|
||||
|
||||
func deleteDatabaseButton(databaseID uuid.UUID) gomponents.Node {
|
||||
func deleteDatabaseButton(databaseID uuid.UUID) nodx.Node {
|
||||
return component.OptionsDropdownButton(
|
||||
htmx.HxDelete("/dashboard/databases/"+databaseID.String()),
|
||||
htmx.HxConfirm("Are you sure you want to delete this database?"),
|
||||
|
||||
@@ -3,16 +3,14 @@ package databases
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
lucide "github.com/eduardolat/gomponents-lucide"
|
||||
"github.com/eduardolat/pgbackweb/internal/database/dbgen"
|
||||
"github.com/eduardolat/pgbackweb/internal/validate"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/component"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/htmx"
|
||||
"github.com/google/uuid"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/components"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
lucide "github.com/nodxdev/nodxgo-lucide"
|
||||
)
|
||||
|
||||
func (h *handlers) editDatabaseHandler(c echo.Context) error {
|
||||
@@ -48,29 +46,29 @@ func (h *handlers) editDatabaseHandler(c echo.Context) error {
|
||||
|
||||
func editDatabaseButton(
|
||||
database dbgen.DatabasesServicePaginateDatabasesRow,
|
||||
) gomponents.Node {
|
||||
) nodx.Node {
|
||||
idPref := "edit-database-" + database.ID.String()
|
||||
formID := idPref + "-form"
|
||||
btnClass := idPref + "-btn"
|
||||
loadingID := idPref + "-loading"
|
||||
|
||||
htmxAttributes := func(url string) gomponents.Node {
|
||||
return gomponents.Group([]gomponents.Node{
|
||||
htmxAttributes := func(url string) nodx.Node {
|
||||
return nodx.Group(
|
||||
htmx.HxPost(url),
|
||||
htmx.HxInclude("#" + formID),
|
||||
htmx.HxDisabledELT("." + btnClass),
|
||||
htmx.HxIndicator("#" + loadingID),
|
||||
htmx.HxInclude("#"+formID),
|
||||
htmx.HxDisabledELT("."+btnClass),
|
||||
htmx.HxIndicator("#"+loadingID),
|
||||
htmx.HxValidate("true"),
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
mo := component.Modal(component.ModalParams{
|
||||
Size: component.SizeMd,
|
||||
Title: "Edit database",
|
||||
Content: []gomponents.Node{
|
||||
html.Form(
|
||||
html.ID(formID),
|
||||
html.Class("space-y-2"),
|
||||
Content: []nodx.Node{
|
||||
nodx.FormEl(
|
||||
nodx.Id(formID),
|
||||
nodx.Class("space-y-2"),
|
||||
|
||||
component.InputControl(component.InputControlParams{
|
||||
Name: "name",
|
||||
@@ -79,8 +77,8 @@ func editDatabaseButton(
|
||||
Required: true,
|
||||
Type: component.InputTypeText,
|
||||
HelpText: "A name to easily identify the database",
|
||||
Children: []gomponents.Node{
|
||||
html.Value(database.Name),
|
||||
Children: []nodx.Node{
|
||||
nodx.Value(database.Name),
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -89,7 +87,7 @@ func editDatabaseButton(
|
||||
Label: "Version",
|
||||
Required: true,
|
||||
HelpText: "The version of the database",
|
||||
Children: []gomponents.Node{
|
||||
Children: []nodx.Node{
|
||||
component.PGVersionSelectOptions(sql.NullString{
|
||||
Valid: true,
|
||||
String: database.PgVersion,
|
||||
@@ -104,36 +102,36 @@ func editDatabaseButton(
|
||||
Required: true,
|
||||
Type: component.InputTypeText,
|
||||
HelpText: "It should be a valid PostgreSQL connection string including the database name. It will be stored securely using PGP encryption.",
|
||||
Children: []gomponents.Node{
|
||||
html.Value(database.DecryptedConnectionString),
|
||||
Children: []nodx.Node{
|
||||
nodx.Value(database.DecryptedConnectionString),
|
||||
},
|
||||
}),
|
||||
),
|
||||
|
||||
html.Div(
|
||||
html.Class("flex justify-between items-center pt-4"),
|
||||
html.Div(
|
||||
html.Button(
|
||||
nodx.Div(
|
||||
nodx.Class("flex justify-between items-center pt-4"),
|
||||
nodx.Div(
|
||||
nodx.Button(
|
||||
htmxAttributes("/dashboard/databases/test"),
|
||||
components.Classes{
|
||||
nodx.ClassMap{
|
||||
btnClass: true,
|
||||
"btn btn-neutral btn-outline": true,
|
||||
},
|
||||
html.Type("button"),
|
||||
nodx.Type("button"),
|
||||
component.SpanText("Test connection"),
|
||||
lucide.DatabaseZap(),
|
||||
),
|
||||
),
|
||||
html.Div(
|
||||
html.Class("flex justify-end items-center space-x-2"),
|
||||
nodx.Div(
|
||||
nodx.Class("flex justify-end items-center space-x-2"),
|
||||
component.HxLoadingMd(loadingID),
|
||||
html.Button(
|
||||
nodx.Button(
|
||||
htmxAttributes("/dashboard/databases/"+database.ID.String()+"/edit"),
|
||||
components.Classes{
|
||||
nodx.ClassMap{
|
||||
btnClass: true,
|
||||
"btn btn-primary": true,
|
||||
},
|
||||
html.Type("button"),
|
||||
nodx.Type("button"),
|
||||
component.SpanText("Save"),
|
||||
lucide.Save(),
|
||||
),
|
||||
@@ -142,7 +140,7 @@ func editDatabaseButton(
|
||||
},
|
||||
})
|
||||
|
||||
return html.Div(
|
||||
return nodx.Div(
|
||||
mo.HTML,
|
||||
component.OptionsDropdownButton(
|
||||
mo.OpenerAttr,
|
||||
|
||||
@@ -9,39 +9,38 @@ import (
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/htmx"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/layout"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
)
|
||||
|
||||
func (h *handlers) indexPageHandler(c echo.Context) error {
|
||||
reqCtx := reqctx.GetCtx(c)
|
||||
return echoutil.RenderGomponent(c, http.StatusOK, indexPage(reqCtx))
|
||||
return echoutil.RenderNodx(c, http.StatusOK, indexPage(reqCtx))
|
||||
}
|
||||
|
||||
func indexPage(reqCtx reqctx.Ctx) gomponents.Node {
|
||||
content := []gomponents.Node{
|
||||
html.Div(
|
||||
html.Class("flex justify-between items-start"),
|
||||
func indexPage(reqCtx reqctx.Ctx) nodx.Node {
|
||||
content := []nodx.Node{
|
||||
nodx.Div(
|
||||
nodx.Class("flex justify-between items-start"),
|
||||
component.H1Text("Databases"),
|
||||
createDatabaseButton(),
|
||||
),
|
||||
component.CardBox(component.CardBoxParams{
|
||||
Class: "mt-4",
|
||||
Children: []gomponents.Node{
|
||||
html.Div(
|
||||
html.Class("overflow-x-auto"),
|
||||
html.Table(
|
||||
html.Class("table text-nowrap"),
|
||||
html.THead(
|
||||
html.Tr(
|
||||
html.Th(html.Class("w-1")),
|
||||
html.Th(component.SpanText("Name")),
|
||||
html.Th(component.SpanText("Version")),
|
||||
html.Th(component.SpanText("Connection string")),
|
||||
html.Th(component.SpanText("Created at")),
|
||||
Children: []nodx.Node{
|
||||
nodx.Div(
|
||||
nodx.Class("overflow-x-auto"),
|
||||
nodx.Table(
|
||||
nodx.Class("table text-nowrap"),
|
||||
nodx.Thead(
|
||||
nodx.Tr(
|
||||
nodx.Th(nodx.Class("w-1")),
|
||||
nodx.Th(component.SpanText("Name")),
|
||||
nodx.Th(component.SpanText("Version")),
|
||||
nodx.Th(component.SpanText("Connection string")),
|
||||
nodx.Th(component.SpanText("Created at")),
|
||||
),
|
||||
),
|
||||
html.TBody(
|
||||
nodx.Tbody(
|
||||
component.SkeletonTr(8),
|
||||
htmx.HxGet("/dashboard/databases/list?page=1"),
|
||||
htmx.HxTrigger("load"),
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
lucide "github.com/eduardolat/gomponents-lucide"
|
||||
"github.com/eduardolat/pgbackweb/internal/database/dbgen"
|
||||
"github.com/eduardolat/pgbackweb/internal/service/databases"
|
||||
"github.com/eduardolat/pgbackweb/internal/util/echoutil"
|
||||
@@ -14,8 +13,8 @@ import (
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/component"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/htmx"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
lucide "github.com/nodxdev/nodxgo-lucide"
|
||||
)
|
||||
|
||||
func (h *handlers) listDatabasesHandler(c echo.Context) error {
|
||||
@@ -41,7 +40,7 @@ func (h *handlers) listDatabasesHandler(c echo.Context) error {
|
||||
return htmx.RespondToastError(c, err.Error())
|
||||
}
|
||||
|
||||
return echoutil.RenderGomponent(
|
||||
return echoutil.RenderNodx(
|
||||
c, http.StatusOK, listDatabases(pagination, databases),
|
||||
)
|
||||
}
|
||||
@@ -49,7 +48,7 @@ func (h *handlers) listDatabasesHandler(c echo.Context) error {
|
||||
func listDatabases(
|
||||
pagination paginateutil.PaginateResponse,
|
||||
databases []dbgen.DatabasesServicePaginateDatabasesRow,
|
||||
) gomponents.Node {
|
||||
) nodx.Node {
|
||||
if len(databases) < 1 {
|
||||
return component.EmptyResultsTr(component.EmptyResultsParams{
|
||||
Title: "No databases found",
|
||||
@@ -57,17 +56,17 @@ func listDatabases(
|
||||
})
|
||||
}
|
||||
|
||||
trs := []gomponents.Node{}
|
||||
trs := []nodx.Node{}
|
||||
for _, database := range databases {
|
||||
trs = append(trs, html.Tr(
|
||||
html.Td(component.OptionsDropdown(
|
||||
html.Div(
|
||||
html.Class("flex flex-col space-y-1"),
|
||||
trs = append(trs, nodx.Tr(
|
||||
nodx.Td(component.OptionsDropdown(
|
||||
nodx.Div(
|
||||
nodx.Class("flex flex-col space-y-1"),
|
||||
component.OptionsDropdownA(
|
||||
html.Href(
|
||||
nodx.Href(
|
||||
fmt.Sprintf("/dashboard/executions?database=%s", database.ID),
|
||||
),
|
||||
html.Target("_blank"),
|
||||
nodx.Target("_blank"),
|
||||
lucide.List(),
|
||||
component.SpanText("Show executions"),
|
||||
),
|
||||
@@ -81,29 +80,29 @@ func listDatabases(
|
||||
deleteDatabaseButton(database.ID),
|
||||
),
|
||||
)),
|
||||
html.Td(
|
||||
html.Div(
|
||||
html.Class("flex items-center space-x-2"),
|
||||
nodx.Td(
|
||||
nodx.Div(
|
||||
nodx.Class("flex items-center space-x-2"),
|
||||
component.HealthStatusPing(
|
||||
database.TestOk, database.TestError, database.LastTestAt,
|
||||
),
|
||||
component.SpanText(database.Name),
|
||||
),
|
||||
),
|
||||
html.Td(component.SpanText("PostgreSQL "+database.PgVersion)),
|
||||
html.Td(
|
||||
html.Class("space-x-1"),
|
||||
nodx.Td(component.SpanText("PostgreSQL "+database.PgVersion)),
|
||||
nodx.Td(
|
||||
nodx.Class("space-x-1"),
|
||||
component.CopyButtonSm(database.DecryptedConnectionString),
|
||||
component.SpanText("****************"),
|
||||
),
|
||||
html.Td(component.SpanText(
|
||||
nodx.Td(component.SpanText(
|
||||
database.CreatedAt.Local().Format(timeutil.LayoutYYYYMMDDHHMMSSPretty),
|
||||
)),
|
||||
))
|
||||
}
|
||||
|
||||
if pagination.HasNextPage {
|
||||
trs = append(trs, html.Tr(
|
||||
trs = append(trs, nodx.Tr(
|
||||
htmx.HxGet(fmt.Sprintf(
|
||||
"/dashboard/databases/list?page=%d", pagination.NextPage,
|
||||
)),
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
package destinations
|
||||
|
||||
import (
|
||||
lucide "github.com/eduardolat/gomponents-lucide"
|
||||
"github.com/eduardolat/pgbackweb/internal/database/dbgen"
|
||||
"github.com/eduardolat/pgbackweb/internal/validate"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/component"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/htmx"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
lucide "github.com/nodxdev/nodxgo-lucide"
|
||||
)
|
||||
|
||||
type createDestinationDTO struct {
|
||||
@@ -48,24 +47,24 @@ func (h *handlers) createDestinationHandler(c echo.Context) error {
|
||||
return htmx.RespondRedirect(c, "/dashboard/destinations")
|
||||
}
|
||||
|
||||
func createDestinationButton() gomponents.Node {
|
||||
htmxAttributes := func(url string) gomponents.Node {
|
||||
return gomponents.Group([]gomponents.Node{
|
||||
func createDestinationButton() nodx.Node {
|
||||
htmxAttributes := func(url string) nodx.Node {
|
||||
return nodx.Group(
|
||||
htmx.HxPost(url),
|
||||
htmx.HxInclude("#create-destination-form"),
|
||||
htmx.HxDisabledELT(".create-destination-btn"),
|
||||
htmx.HxIndicator("#create-destination-loading"),
|
||||
htmx.HxValidate("true"),
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
mo := component.Modal(component.ModalParams{
|
||||
Size: component.SizeMd,
|
||||
Title: "Create destination",
|
||||
Content: []gomponents.Node{
|
||||
html.Form(
|
||||
html.ID("create-destination-form"),
|
||||
html.Class("space-y-2"),
|
||||
Content: []nodx.Node{
|
||||
nodx.FormEl(
|
||||
nodx.Id("create-destination-form"),
|
||||
nodx.Class("space-y-2"),
|
||||
|
||||
component.InputControl(component.InputControlParams{
|
||||
Name: "name",
|
||||
@@ -119,24 +118,24 @@ func createDestinationButton() gomponents.Node {
|
||||
}),
|
||||
),
|
||||
|
||||
html.Div(
|
||||
html.Class("flex justify-between items-center pt-4"),
|
||||
html.Div(
|
||||
html.Button(
|
||||
nodx.Div(
|
||||
nodx.Class("flex justify-between items-center pt-4"),
|
||||
nodx.Div(
|
||||
nodx.Button(
|
||||
htmxAttributes("/dashboard/destinations/test"),
|
||||
html.Class("create-destination-btn btn btn-neutral btn-outline"),
|
||||
html.Type("button"),
|
||||
nodx.Class("create-destination-btn btn btn-neutral btn-outline"),
|
||||
nodx.Type("button"),
|
||||
component.SpanText("Test connection"),
|
||||
lucide.PlugZap(),
|
||||
),
|
||||
),
|
||||
html.Div(
|
||||
html.Class("flex justify-end items-center space-x-2"),
|
||||
nodx.Div(
|
||||
nodx.Class("flex justify-end items-center space-x-2"),
|
||||
component.HxLoadingMd("create-destination-loading"),
|
||||
html.Button(
|
||||
nodx.Button(
|
||||
htmxAttributes("/dashboard/destinations"),
|
||||
html.Class("create-destination-btn btn btn-primary"),
|
||||
html.Type("button"),
|
||||
nodx.Class("create-destination-btn btn btn-primary"),
|
||||
nodx.Type("button"),
|
||||
component.SpanText("Save"),
|
||||
lucide.Save(),
|
||||
),
|
||||
@@ -145,15 +144,15 @@ func createDestinationButton() gomponents.Node {
|
||||
},
|
||||
})
|
||||
|
||||
button := html.Button(
|
||||
button := nodx.Button(
|
||||
mo.OpenerAttr,
|
||||
html.Class("btn btn-primary"),
|
||||
nodx.Class("btn btn-primary"),
|
||||
component.SpanText("Create destination"),
|
||||
lucide.Plus(),
|
||||
)
|
||||
|
||||
return html.Div(
|
||||
html.Class("inline-block"),
|
||||
return nodx.Div(
|
||||
nodx.Class("inline-block"),
|
||||
mo.HTML,
|
||||
button,
|
||||
)
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package destinations
|
||||
|
||||
import (
|
||||
lucide "github.com/eduardolat/gomponents-lucide"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/component"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/htmx"
|
||||
"github.com/google/uuid"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/maragudk/gomponents"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
lucide "github.com/nodxdev/nodxgo-lucide"
|
||||
)
|
||||
|
||||
func (h *handlers) deleteDestinationHandler(c echo.Context) error {
|
||||
@@ -25,7 +25,7 @@ func (h *handlers) deleteDestinationHandler(c echo.Context) error {
|
||||
return htmx.RespondRefresh(c)
|
||||
}
|
||||
|
||||
func deleteDestinationButton(destinationID uuid.UUID) gomponents.Node {
|
||||
func deleteDestinationButton(destinationID uuid.UUID) nodx.Node {
|
||||
return component.OptionsDropdownButton(
|
||||
htmx.HxDelete("/dashboard/destinations/"+destinationID.String()),
|
||||
htmx.HxConfirm("Are you sure you want to delete this destination?"),
|
||||
|
||||
@@ -3,16 +3,14 @@ package destinations
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
lucide "github.com/eduardolat/gomponents-lucide"
|
||||
"github.com/eduardolat/pgbackweb/internal/database/dbgen"
|
||||
"github.com/eduardolat/pgbackweb/internal/validate"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/component"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/htmx"
|
||||
"github.com/google/uuid"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/components"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
lucide "github.com/nodxdev/nodxgo-lucide"
|
||||
)
|
||||
|
||||
func (h *handlers) editDestinationHandler(c echo.Context) error {
|
||||
@@ -51,29 +49,29 @@ func (h *handlers) editDestinationHandler(c echo.Context) error {
|
||||
|
||||
func editDestinationButton(
|
||||
destination dbgen.DestinationsServicePaginateDestinationsRow,
|
||||
) gomponents.Node {
|
||||
) nodx.Node {
|
||||
idPref := "edit-destination-" + destination.ID.String()
|
||||
formID := idPref + "-form"
|
||||
btnClass := idPref + "-btn"
|
||||
loadingID := idPref + "-loading"
|
||||
|
||||
htmxAttributes := func(url string) gomponents.Node {
|
||||
return gomponents.Group([]gomponents.Node{
|
||||
htmxAttributes := func(url string) nodx.Node {
|
||||
return nodx.Group(
|
||||
htmx.HxPost(url),
|
||||
htmx.HxInclude("#" + formID),
|
||||
htmx.HxDisabledELT("." + btnClass),
|
||||
htmx.HxIndicator("#" + loadingID),
|
||||
htmx.HxInclude("#"+formID),
|
||||
htmx.HxDisabledELT("."+btnClass),
|
||||
htmx.HxIndicator("#"+loadingID),
|
||||
htmx.HxValidate("true"),
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
mo := component.Modal(component.ModalParams{
|
||||
Size: component.SizeMd,
|
||||
Title: "Edit destination",
|
||||
Content: []gomponents.Node{
|
||||
html.Form(
|
||||
html.ID(formID),
|
||||
html.Class("space-y-2"),
|
||||
Content: []nodx.Node{
|
||||
nodx.FormEl(
|
||||
nodx.Id(formID),
|
||||
nodx.Class("space-y-2"),
|
||||
|
||||
component.InputControl(component.InputControlParams{
|
||||
Name: "name",
|
||||
@@ -82,8 +80,8 @@ func editDestinationButton(
|
||||
Required: true,
|
||||
Type: component.InputTypeText,
|
||||
HelpText: "A name to easily identify the destination",
|
||||
Children: []gomponents.Node{
|
||||
html.Value(destination.Name),
|
||||
Children: []nodx.Node{
|
||||
nodx.Value(destination.Name),
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -93,8 +91,8 @@ func editDestinationButton(
|
||||
Placeholder: "my-bucket",
|
||||
Required: true,
|
||||
Type: component.InputTypeText,
|
||||
Children: []gomponents.Node{
|
||||
html.Value(destination.BucketName),
|
||||
Children: []nodx.Node{
|
||||
nodx.Value(destination.BucketName),
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -104,8 +102,8 @@ func editDestinationButton(
|
||||
Placeholder: "s3-us-west-1.amazonaws.com",
|
||||
Required: true,
|
||||
Type: component.InputTypeText,
|
||||
Children: []gomponents.Node{
|
||||
html.Value(destination.Endpoint),
|
||||
Children: []nodx.Node{
|
||||
nodx.Value(destination.Endpoint),
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -115,8 +113,8 @@ func editDestinationButton(
|
||||
Placeholder: "us-west-1",
|
||||
Required: true,
|
||||
Type: component.InputTypeText,
|
||||
Children: []gomponents.Node{
|
||||
html.Value(destination.Region),
|
||||
Children: []nodx.Node{
|
||||
nodx.Value(destination.Region),
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -127,8 +125,8 @@ func editDestinationButton(
|
||||
Required: true,
|
||||
Type: component.InputTypeText,
|
||||
HelpText: "It will be stored securely using PGP encryption.",
|
||||
Children: []gomponents.Node{
|
||||
html.Value(destination.DecryptedAccessKey),
|
||||
Children: []nodx.Node{
|
||||
nodx.Value(destination.DecryptedAccessKey),
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -139,36 +137,36 @@ func editDestinationButton(
|
||||
Required: true,
|
||||
Type: component.InputTypeText,
|
||||
HelpText: "It will be stored securely using PGP encryption.",
|
||||
Children: []gomponents.Node{
|
||||
html.Value(destination.DecryptedSecretKey),
|
||||
Children: []nodx.Node{
|
||||
nodx.Value(destination.DecryptedSecretKey),
|
||||
},
|
||||
}),
|
||||
),
|
||||
|
||||
html.Div(
|
||||
html.Class("flex justify-between items-center pt-4"),
|
||||
html.Div(
|
||||
html.Button(
|
||||
nodx.Div(
|
||||
nodx.Class("flex justify-between items-center pt-4"),
|
||||
nodx.Div(
|
||||
nodx.Button(
|
||||
htmxAttributes("/dashboard/destinations/test"),
|
||||
components.Classes{
|
||||
nodx.ClassMap{
|
||||
btnClass: true,
|
||||
"btn btn-neutral btn-outline": true,
|
||||
},
|
||||
html.Type("button"),
|
||||
nodx.Type("button"),
|
||||
component.SpanText("Test connection"),
|
||||
lucide.PlugZap(),
|
||||
),
|
||||
),
|
||||
html.Div(
|
||||
html.Class("flex justify-end items-center space-x-2"),
|
||||
nodx.Div(
|
||||
nodx.Class("flex justify-end items-center space-x-2"),
|
||||
component.HxLoadingMd(loadingID),
|
||||
html.Button(
|
||||
nodx.Button(
|
||||
htmxAttributes("/dashboard/destinations/"+destination.ID.String()+"/edit"),
|
||||
components.Classes{
|
||||
nodx.ClassMap{
|
||||
btnClass: true,
|
||||
"btn btn-primary": true,
|
||||
},
|
||||
html.Type("button"),
|
||||
nodx.Type("button"),
|
||||
component.SpanText("Save"),
|
||||
lucide.Save(),
|
||||
),
|
||||
@@ -177,7 +175,7 @@ func editDestinationButton(
|
||||
},
|
||||
})
|
||||
|
||||
return html.Div(
|
||||
return nodx.Div(
|
||||
mo.HTML,
|
||||
component.OptionsDropdownButton(
|
||||
mo.OpenerAttr,
|
||||
|
||||
@@ -9,52 +9,51 @@ import (
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/htmx"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/layout"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
)
|
||||
|
||||
func (h *handlers) indexPageHandler(c echo.Context) error {
|
||||
reqCtx := reqctx.GetCtx(c)
|
||||
return echoutil.RenderGomponent(c, http.StatusOK, indexPage(reqCtx))
|
||||
return echoutil.RenderNodx(c, http.StatusOK, indexPage(reqCtx))
|
||||
}
|
||||
|
||||
func indexPage(reqCtx reqctx.Ctx) gomponents.Node {
|
||||
content := []gomponents.Node{
|
||||
html.Div(
|
||||
html.Class("flex justify-between items-start space-x-2"),
|
||||
html.Div(
|
||||
func indexPage(reqCtx reqctx.Ctx) nodx.Node {
|
||||
content := []nodx.Node{
|
||||
nodx.Div(
|
||||
nodx.Class("flex justify-between items-start space-x-2"),
|
||||
nodx.Div(
|
||||
component.H1Text("S3 Destinations"),
|
||||
component.PText(`
|
||||
Here you can manage your S3 destinations. You can skip creating a S3
|
||||
destination if you want to use the local storage for your backups.
|
||||
`),
|
||||
),
|
||||
html.Div(
|
||||
html.Class("flex-none"),
|
||||
nodx.Div(
|
||||
nodx.Class("flex-none"),
|
||||
createDestinationButton(),
|
||||
),
|
||||
),
|
||||
|
||||
component.CardBox(component.CardBoxParams{
|
||||
Class: "mt-4",
|
||||
Children: []gomponents.Node{
|
||||
html.Div(
|
||||
html.Class("overflow-x-auto"),
|
||||
html.Table(
|
||||
html.Class("table text-nowrap"),
|
||||
html.THead(
|
||||
html.Tr(
|
||||
html.Th(html.Class("w-1")),
|
||||
html.Th(component.SpanText("Name")),
|
||||
html.Th(component.SpanText("Bucket name")),
|
||||
html.Th(component.SpanText("Endpoint")),
|
||||
html.Th(component.SpanText("Region")),
|
||||
html.Th(component.SpanText("Access key")),
|
||||
html.Th(component.SpanText("Secret key")),
|
||||
html.Th(component.SpanText("Created at")),
|
||||
Children: []nodx.Node{
|
||||
nodx.Div(
|
||||
nodx.Class("overflow-x-auto"),
|
||||
nodx.Table(
|
||||
nodx.Class("table text-nowrap"),
|
||||
nodx.Thead(
|
||||
nodx.Tr(
|
||||
nodx.Th(nodx.Class("w-1")),
|
||||
nodx.Th(component.SpanText("Name")),
|
||||
nodx.Th(component.SpanText("Bucket name")),
|
||||
nodx.Th(component.SpanText("Endpoint")),
|
||||
nodx.Th(component.SpanText("Region")),
|
||||
nodx.Th(component.SpanText("Access key")),
|
||||
nodx.Th(component.SpanText("Secret key")),
|
||||
nodx.Th(component.SpanText("Created at")),
|
||||
),
|
||||
),
|
||||
html.TBody(
|
||||
nodx.Tbody(
|
||||
component.SkeletonTr(8),
|
||||
htmx.HxGet("/dashboard/destinations/list?page=1"),
|
||||
htmx.HxTrigger("load"),
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
lucide "github.com/eduardolat/gomponents-lucide"
|
||||
"github.com/eduardolat/pgbackweb/internal/database/dbgen"
|
||||
"github.com/eduardolat/pgbackweb/internal/service/destinations"
|
||||
"github.com/eduardolat/pgbackweb/internal/util/echoutil"
|
||||
@@ -14,8 +13,8 @@ import (
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/component"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/htmx"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
lucide "github.com/nodxdev/nodxgo-lucide"
|
||||
)
|
||||
|
||||
func (h *handlers) listDestinationsHandler(c echo.Context) error {
|
||||
@@ -41,7 +40,7 @@ func (h *handlers) listDestinationsHandler(c echo.Context) error {
|
||||
return htmx.RespondToastError(c, err.Error())
|
||||
}
|
||||
|
||||
return echoutil.RenderGomponent(
|
||||
return echoutil.RenderNodx(
|
||||
c, http.StatusOK, listDestinations(pagination, destinations),
|
||||
)
|
||||
}
|
||||
@@ -49,7 +48,7 @@ func (h *handlers) listDestinationsHandler(c echo.Context) error {
|
||||
func listDestinations(
|
||||
pagination paginateutil.PaginateResponse,
|
||||
destinations []dbgen.DestinationsServicePaginateDestinationsRow,
|
||||
) gomponents.Node {
|
||||
) nodx.Node {
|
||||
if len(destinations) < 1 {
|
||||
return component.EmptyResultsTr(component.EmptyResultsParams{
|
||||
Title: "No destinations found",
|
||||
@@ -57,15 +56,15 @@ func listDestinations(
|
||||
})
|
||||
}
|
||||
|
||||
trs := []gomponents.Node{}
|
||||
trs := []nodx.Node{}
|
||||
for _, destination := range destinations {
|
||||
trs = append(trs, html.Tr(
|
||||
html.Td(component.OptionsDropdown(
|
||||
trs = append(trs, nodx.Tr(
|
||||
nodx.Td(component.OptionsDropdown(
|
||||
component.OptionsDropdownA(
|
||||
html.Href(
|
||||
nodx.Href(
|
||||
fmt.Sprintf("/dashboard/executions?destination=%s", destination.ID),
|
||||
),
|
||||
html.Target("_blank"),
|
||||
nodx.Target("_blank"),
|
||||
lucide.List(),
|
||||
component.SpanText("Show executions"),
|
||||
),
|
||||
@@ -78,58 +77,58 @@ func listDestinations(
|
||||
),
|
||||
deleteDestinationButton(destination.ID),
|
||||
)),
|
||||
html.Td(
|
||||
html.Div(
|
||||
html.Class("flex items-center space-x-2"),
|
||||
nodx.Td(
|
||||
nodx.Div(
|
||||
nodx.Class("flex items-center space-x-2"),
|
||||
component.HealthStatusPing(
|
||||
destination.TestOk, destination.TestError, destination.LastTestAt,
|
||||
),
|
||||
component.SpanText(destination.Name),
|
||||
),
|
||||
),
|
||||
html.Td(
|
||||
html.Div(
|
||||
html.Class("flex items-center space-x-1"),
|
||||
nodx.Td(
|
||||
nodx.Div(
|
||||
nodx.Class("flex items-center space-x-1"),
|
||||
component.CopyButtonSm(destination.BucketName),
|
||||
component.SpanText(destination.BucketName),
|
||||
),
|
||||
),
|
||||
html.Td(
|
||||
html.Div(
|
||||
html.Class("flex items-center space-x-1"),
|
||||
nodx.Td(
|
||||
nodx.Div(
|
||||
nodx.Class("flex items-center space-x-1"),
|
||||
component.CopyButtonSm(destination.Endpoint),
|
||||
component.SpanText(destination.Endpoint),
|
||||
),
|
||||
),
|
||||
html.Td(
|
||||
html.Div(
|
||||
html.Class("flex items-center space-x-1"),
|
||||
nodx.Td(
|
||||
nodx.Div(
|
||||
nodx.Class("flex items-center space-x-1"),
|
||||
component.CopyButtonSm(destination.Region),
|
||||
component.SpanText(destination.Region),
|
||||
),
|
||||
),
|
||||
html.Td(
|
||||
html.Div(
|
||||
html.Class("flex items-center space-x-1"),
|
||||
nodx.Td(
|
||||
nodx.Div(
|
||||
nodx.Class("flex items-center space-x-1"),
|
||||
component.CopyButtonSm(destination.DecryptedAccessKey),
|
||||
component.SpanText("**********"),
|
||||
),
|
||||
),
|
||||
html.Td(
|
||||
html.Div(
|
||||
html.Class("flex items-center space-x-1"),
|
||||
nodx.Td(
|
||||
nodx.Div(
|
||||
nodx.Class("flex items-center space-x-1"),
|
||||
component.CopyButtonSm(destination.DecryptedSecretKey),
|
||||
component.SpanText("**********"),
|
||||
),
|
||||
),
|
||||
html.Td(component.SpanText(
|
||||
nodx.Td(component.SpanText(
|
||||
destination.CreatedAt.Local().Format(timeutil.LayoutYYYYMMDDHHMMSSPretty),
|
||||
)),
|
||||
))
|
||||
}
|
||||
|
||||
if pagination.HasNextPage {
|
||||
trs = append(trs, html.Tr(
|
||||
trs = append(trs, nodx.Tr(
|
||||
htmx.HxGet(fmt.Sprintf(
|
||||
"/dashboard/destinations/list?page=%d", pagination.NextPage,
|
||||
)),
|
||||
|
||||
@@ -12,8 +12,7 @@ import (
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/layout"
|
||||
"github.com/google/uuid"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
)
|
||||
|
||||
type execsQueryData struct {
|
||||
@@ -33,33 +32,33 @@ func (h *handlers) indexPageHandler(c echo.Context) error {
|
||||
return c.String(http.StatusBadRequest, err.Error())
|
||||
}
|
||||
|
||||
return echoutil.RenderGomponent(c, http.StatusOK, indexPage(reqCtx, queryData))
|
||||
return echoutil.RenderNodx(c, http.StatusOK, indexPage(reqCtx, queryData))
|
||||
}
|
||||
|
||||
func indexPage(reqCtx reqctx.Ctx, queryData execsQueryData) gomponents.Node {
|
||||
content := []gomponents.Node{
|
||||
func indexPage(reqCtx reqctx.Ctx, queryData execsQueryData) nodx.Node {
|
||||
content := []nodx.Node{
|
||||
component.H1Text("Executions"),
|
||||
component.CardBox(component.CardBoxParams{
|
||||
Class: "mt-4",
|
||||
Children: []gomponents.Node{
|
||||
html.Div(
|
||||
html.Class("overflow-x-auto"),
|
||||
html.Table(
|
||||
html.Class("table text-nowrap"),
|
||||
html.THead(
|
||||
html.Tr(
|
||||
html.Th(html.Class("w-1")),
|
||||
html.Th(component.SpanText("Status")),
|
||||
html.Th(component.SpanText("Backup")),
|
||||
html.Th(component.SpanText("Database")),
|
||||
html.Th(component.SpanText("Destination")),
|
||||
html.Th(component.SpanText("Started at")),
|
||||
html.Th(component.SpanText("Finished at")),
|
||||
html.Th(component.SpanText("Duration")),
|
||||
html.Th(component.SpanText("File size")),
|
||||
Children: []nodx.Node{
|
||||
nodx.Div(
|
||||
nodx.Class("overflow-x-auto"),
|
||||
nodx.Table(
|
||||
nodx.Class("table text-nowrap"),
|
||||
nodx.Thead(
|
||||
nodx.Tr(
|
||||
nodx.Th(nodx.Class("w-1")),
|
||||
nodx.Th(component.SpanText("Status")),
|
||||
nodx.Th(component.SpanText("Backup")),
|
||||
nodx.Th(component.SpanText("Database")),
|
||||
nodx.Th(component.SpanText("Destination")),
|
||||
nodx.Th(component.SpanText("Started at")),
|
||||
nodx.Th(component.SpanText("Finished at")),
|
||||
nodx.Th(component.SpanText("Duration")),
|
||||
nodx.Th(component.SpanText("File size")),
|
||||
),
|
||||
),
|
||||
html.TBody(
|
||||
nodx.Tbody(
|
||||
component.SkeletonTr(8),
|
||||
htmx.HxGet(func() string {
|
||||
url := "/dashboard/executions/list?page=1"
|
||||
|
||||
@@ -15,8 +15,7 @@ import (
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/htmx"
|
||||
"github.com/google/uuid"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
)
|
||||
|
||||
type listExecsQueryData struct {
|
||||
@@ -56,7 +55,7 @@ func (h *handlers) listExecutionsHandler(c echo.Context) error {
|
||||
return htmx.RespondToastError(c, err.Error())
|
||||
}
|
||||
|
||||
return echoutil.RenderGomponent(
|
||||
return echoutil.RenderNodx(
|
||||
c, http.StatusOK, listExecutions(queryData, pagination, executions),
|
||||
)
|
||||
}
|
||||
@@ -65,7 +64,7 @@ func listExecutions(
|
||||
queryData listExecsQueryData,
|
||||
pagination paginateutil.PaginateResponse,
|
||||
executions []dbgen.ExecutionsServicePaginateExecutionsRow,
|
||||
) gomponents.Node {
|
||||
) nodx.Node {
|
||||
if len(executions) < 1 {
|
||||
return component.EmptyResultsTr(component.EmptyResultsParams{
|
||||
Title: "No executions found",
|
||||
@@ -73,40 +72,40 @@ func listExecutions(
|
||||
})
|
||||
}
|
||||
|
||||
trs := []gomponents.Node{}
|
||||
trs := []nodx.Node{}
|
||||
for _, execution := range executions {
|
||||
trs = append(trs, html.Tr(
|
||||
html.Td(component.OptionsDropdown(
|
||||
trs = append(trs, nodx.Tr(
|
||||
nodx.Td(component.OptionsDropdown(
|
||||
showExecutionButton(execution),
|
||||
restoreExecutionButton(execution),
|
||||
)),
|
||||
html.Td(component.StatusBadge(execution.Status)),
|
||||
html.Td(component.SpanText(execution.BackupName)),
|
||||
html.Td(component.SpanText(execution.DatabaseName)),
|
||||
html.Td(component.PrettyDestinationName(
|
||||
nodx.Td(component.StatusBadge(execution.Status)),
|
||||
nodx.Td(component.SpanText(execution.BackupName)),
|
||||
nodx.Td(component.SpanText(execution.DatabaseName)),
|
||||
nodx.Td(component.PrettyDestinationName(
|
||||
execution.BackupIsLocal, execution.DestinationName,
|
||||
)),
|
||||
html.Td(component.SpanText(
|
||||
nodx.Td(component.SpanText(
|
||||
execution.StartedAt.Local().Format(timeutil.LayoutYYYYMMDDHHMMSSPretty),
|
||||
)),
|
||||
html.Td(
|
||||
gomponents.If(
|
||||
nodx.Td(
|
||||
nodx.If(
|
||||
execution.FinishedAt.Valid,
|
||||
component.SpanText(
|
||||
execution.FinishedAt.Time.Local().Format(timeutil.LayoutYYYYMMDDHHMMSSPretty),
|
||||
),
|
||||
),
|
||||
),
|
||||
html.Td(
|
||||
gomponents.If(
|
||||
nodx.Td(
|
||||
nodx.If(
|
||||
execution.FinishedAt.Valid,
|
||||
component.SpanText(
|
||||
execution.FinishedAt.Time.Sub(execution.StartedAt).String(),
|
||||
),
|
||||
),
|
||||
),
|
||||
html.Td(
|
||||
gomponents.If(
|
||||
nodx.Td(
|
||||
nodx.If(
|
||||
execution.FileSize.Valid,
|
||||
component.PrettyFileSize(execution.FileSize),
|
||||
),
|
||||
@@ -115,7 +114,7 @@ func listExecutions(
|
||||
}
|
||||
|
||||
if pagination.HasNextPage {
|
||||
trs = append(trs, html.Tr(
|
||||
trs = append(trs, nodx.Tr(
|
||||
htmx.HxGet(func() string {
|
||||
url := "/dashboard/executions/list"
|
||||
url = strutil.AddQueryParamToUrl(url, "page", fmt.Sprintf("%d", pagination.NextPage))
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
lucide "github.com/eduardolat/gomponents-lucide"
|
||||
"github.com/eduardolat/pgbackweb/internal/database/dbgen"
|
||||
"github.com/eduardolat/pgbackweb/internal/util/echoutil"
|
||||
"github.com/eduardolat/pgbackweb/internal/validate"
|
||||
@@ -14,8 +13,8 @@ import (
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/htmx"
|
||||
"github.com/google/uuid"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
lucide "github.com/nodxdev/nodxgo-lucide"
|
||||
)
|
||||
|
||||
func (h *handlers) restoreExecutionHandler(c echo.Context) error {
|
||||
@@ -97,7 +96,7 @@ func (h *handlers) restoreExecutionFormHandler(c echo.Context) error {
|
||||
return c.String(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
return echoutil.RenderGomponent(c, http.StatusOK, restoreExecutionForm(
|
||||
return echoutil.RenderNodx(c, http.StatusOK, restoreExecutionForm(
|
||||
execution, databases,
|
||||
))
|
||||
}
|
||||
@@ -105,38 +104,38 @@ func (h *handlers) restoreExecutionFormHandler(c echo.Context) error {
|
||||
func restoreExecutionForm(
|
||||
execution dbgen.ExecutionsServiceGetExecutionRow,
|
||||
databases []dbgen.DatabasesServiceGetAllDatabasesRow,
|
||||
) gomponents.Node {
|
||||
return html.Form(
|
||||
) nodx.Node {
|
||||
return nodx.FormEl(
|
||||
htmx.HxPost("/dashboard/executions/"+execution.ID.String()+"/restore"),
|
||||
htmx.HxConfirm("Are you sure you want to restore this backup?"),
|
||||
htmx.HxDisabledELT("find button"),
|
||||
|
||||
alpine.XData(`{ backup_to: "database" }`),
|
||||
|
||||
html.Input(
|
||||
html.Type("hidden"),
|
||||
html.Name("execution_id"),
|
||||
html.Value(execution.ID.String()),
|
||||
nodx.Input(
|
||||
nodx.Type("hidden"),
|
||||
nodx.Name("execution_id"),
|
||||
nodx.Value(execution.ID.String()),
|
||||
),
|
||||
|
||||
html.Div(
|
||||
html.Class("space-y-2 text-base"),
|
||||
nodx.Div(
|
||||
nodx.Class("space-y-2 text-base"),
|
||||
|
||||
component.SelectControl(component.SelectControlParams{
|
||||
Name: "backup_to",
|
||||
Label: "Backup to",
|
||||
Required: true,
|
||||
HelpText: "You can restore the backup to an existing database or any other database using a connection string",
|
||||
Children: []gomponents.Node{
|
||||
Children: []nodx.Node{
|
||||
alpine.XModel("backup_to"),
|
||||
html.Option(
|
||||
html.Value("database"),
|
||||
gomponents.Text("Existing database"),
|
||||
html.Selected(),
|
||||
nodx.Option(
|
||||
nodx.Value("database"),
|
||||
nodx.Text("Existing database"),
|
||||
nodx.Selected(""),
|
||||
),
|
||||
html.Option(
|
||||
html.Value("conn_string"),
|
||||
gomponents.Text("Other database"),
|
||||
nodx.Option(
|
||||
nodx.Value("conn_string"),
|
||||
nodx.Text("Other database"),
|
||||
),
|
||||
},
|
||||
}),
|
||||
@@ -148,16 +147,16 @@ func restoreExecutionForm(
|
||||
Label: "Database",
|
||||
Placeholder: "Select a database",
|
||||
Required: true,
|
||||
Children: []gomponents.Node{
|
||||
component.GMap(
|
||||
Children: []nodx.Node{
|
||||
nodx.Map(
|
||||
databases,
|
||||
func(db dbgen.DatabasesServiceGetAllDatabasesRow) gomponents.Node {
|
||||
return html.Option(
|
||||
html.Value(db.ID.String()),
|
||||
gomponents.Text(db.Name),
|
||||
gomponents.If(
|
||||
func(db dbgen.DatabasesServiceGetAllDatabasesRow) nodx.Node {
|
||||
return nodx.Option(
|
||||
nodx.Value(db.ID.String()),
|
||||
nodx.Text(db.Name),
|
||||
nodx.If(
|
||||
db.ID == execution.DatabaseID,
|
||||
html.Selected(),
|
||||
nodx.Selected(""),
|
||||
),
|
||||
)
|
||||
},
|
||||
@@ -177,14 +176,14 @@ func restoreExecutionForm(
|
||||
}),
|
||||
),
|
||||
|
||||
html.Div(
|
||||
html.Class("pt-2"),
|
||||
html.Div(
|
||||
html.Role("alert"),
|
||||
html.Class("alert alert-warning"),
|
||||
nodx.Div(
|
||||
nodx.Class("pt-2"),
|
||||
nodx.Div(
|
||||
nodx.Role("alert"),
|
||||
nodx.Class("alert alert-warning"),
|
||||
lucide.TriangleAlert(),
|
||||
html.Div(
|
||||
html.P(
|
||||
nodx.Div(
|
||||
nodx.P(
|
||||
component.BText(fmt.Sprintf(
|
||||
"This restoration uses psql v%s", execution.DatabasePgVersion,
|
||||
)),
|
||||
@@ -198,12 +197,12 @@ func restoreExecutionForm(
|
||||
),
|
||||
),
|
||||
|
||||
html.Div(
|
||||
html.Class("flex justify-end items-center space-x-2 pt-2"),
|
||||
nodx.Div(
|
||||
nodx.Class("flex justify-end items-center space-x-2 pt-2"),
|
||||
component.HxLoadingMd(),
|
||||
html.Button(
|
||||
html.Class("btn btn-primary"),
|
||||
html.Type("submit"),
|
||||
nodx.Button(
|
||||
nodx.Class("btn btn-primary"),
|
||||
nodx.Type("submit"),
|
||||
component.SpanText("Start restoration"),
|
||||
lucide.Zap(),
|
||||
),
|
||||
@@ -212,7 +211,7 @@ func restoreExecutionForm(
|
||||
)
|
||||
}
|
||||
|
||||
func restoreExecutionButton(execution dbgen.ExecutionsServicePaginateExecutionsRow) gomponents.Node {
|
||||
func restoreExecutionButton(execution dbgen.ExecutionsServicePaginateExecutionsRow) nodx.Node {
|
||||
if execution.Status != "success" || !execution.Path.Valid {
|
||||
return nil
|
||||
}
|
||||
@@ -220,18 +219,18 @@ func restoreExecutionButton(execution dbgen.ExecutionsServicePaginateExecutionsR
|
||||
mo := component.Modal(component.ModalParams{
|
||||
Size: component.SizeMd,
|
||||
Title: "Restore backup execution",
|
||||
Content: []gomponents.Node{
|
||||
html.Div(
|
||||
Content: []nodx.Node{
|
||||
nodx.Div(
|
||||
htmx.HxGet("/dashboard/executions/"+execution.ID.String()+"/restore-form"),
|
||||
htmx.HxSwap("outerHTML"),
|
||||
htmx.HxTrigger("intersect once"),
|
||||
html.Class("p-10 flex justify-center"),
|
||||
nodx.Class("p-10 flex justify-center"),
|
||||
component.HxLoadingMd(),
|
||||
),
|
||||
},
|
||||
})
|
||||
|
||||
return html.Div(
|
||||
return nodx.Div(
|
||||
mo.HTML,
|
||||
component.OptionsDropdownButton(
|
||||
mo.OpenerAttr,
|
||||
|
||||
@@ -4,14 +4,13 @@ import (
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
|
||||
lucide "github.com/eduardolat/gomponents-lucide"
|
||||
"github.com/eduardolat/pgbackweb/internal/database/dbgen"
|
||||
"github.com/eduardolat/pgbackweb/internal/util/timeutil"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/component"
|
||||
"github.com/google/uuid"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
lucide "github.com/nodxdev/nodxgo-lucide"
|
||||
)
|
||||
|
||||
func (h *handlers) downloadExecutionHandler(c echo.Context) error {
|
||||
@@ -38,93 +37,93 @@ func (h *handlers) downloadExecutionHandler(c echo.Context) error {
|
||||
|
||||
func showExecutionButton(
|
||||
execution dbgen.ExecutionsServicePaginateExecutionsRow,
|
||||
) gomponents.Node {
|
||||
) nodx.Node {
|
||||
mo := component.Modal(component.ModalParams{
|
||||
Title: "Execution details",
|
||||
Size: component.SizeMd,
|
||||
Content: []gomponents.Node{
|
||||
html.Div(
|
||||
html.Class("overflow-x-auto"),
|
||||
html.Table(
|
||||
html.Class("table [&_th]:text-nowrap"),
|
||||
html.Tr(
|
||||
html.Th(component.SpanText("ID")),
|
||||
html.Td(component.SpanText(execution.ID.String())),
|
||||
Content: []nodx.Node{
|
||||
nodx.Div(
|
||||
nodx.Class("overflow-x-auto"),
|
||||
nodx.Table(
|
||||
nodx.Class("table [&_th]:text-nowrap"),
|
||||
nodx.Tr(
|
||||
nodx.Th(component.SpanText("ID")),
|
||||
nodx.Td(component.SpanText(execution.ID.String())),
|
||||
),
|
||||
html.Tr(
|
||||
html.Th(component.SpanText("Status")),
|
||||
html.Td(component.StatusBadge(execution.Status)),
|
||||
nodx.Tr(
|
||||
nodx.Th(component.SpanText("Status")),
|
||||
nodx.Td(component.StatusBadge(execution.Status)),
|
||||
),
|
||||
html.Tr(
|
||||
html.Th(component.SpanText("Database")),
|
||||
html.Td(component.SpanText(execution.DatabaseName)),
|
||||
nodx.Tr(
|
||||
nodx.Th(component.SpanText("Database")),
|
||||
nodx.Td(component.SpanText(execution.DatabaseName)),
|
||||
),
|
||||
html.Tr(
|
||||
html.Th(component.SpanText("Destination")),
|
||||
html.Td(component.PrettyDestinationName(
|
||||
nodx.Tr(
|
||||
nodx.Th(component.SpanText("Destination")),
|
||||
nodx.Td(component.PrettyDestinationName(
|
||||
execution.BackupIsLocal, execution.DestinationName,
|
||||
)),
|
||||
),
|
||||
gomponents.If(
|
||||
nodx.If(
|
||||
execution.Message.Valid,
|
||||
html.Tr(
|
||||
html.Th(component.SpanText("Message")),
|
||||
html.Td(
|
||||
html.Class("break-all"),
|
||||
nodx.Tr(
|
||||
nodx.Th(component.SpanText("Message")),
|
||||
nodx.Td(
|
||||
nodx.Class("break-all"),
|
||||
component.SpanText(execution.Message.String),
|
||||
),
|
||||
),
|
||||
),
|
||||
html.Tr(
|
||||
html.Th(component.SpanText("Started at")),
|
||||
html.Td(component.SpanText(
|
||||
nodx.Tr(
|
||||
nodx.Th(component.SpanText("Started at")),
|
||||
nodx.Td(component.SpanText(
|
||||
execution.StartedAt.Local().Format(timeutil.LayoutYYYYMMDDHHMMSSPretty),
|
||||
)),
|
||||
),
|
||||
gomponents.If(
|
||||
nodx.If(
|
||||
execution.FinishedAt.Valid,
|
||||
html.Tr(
|
||||
html.Th(component.SpanText("Finished at")),
|
||||
html.Td(component.SpanText(
|
||||
nodx.Tr(
|
||||
nodx.Th(component.SpanText("Finished at")),
|
||||
nodx.Td(component.SpanText(
|
||||
execution.FinishedAt.Time.Local().Format(timeutil.LayoutYYYYMMDDHHMMSSPretty),
|
||||
)),
|
||||
),
|
||||
),
|
||||
gomponents.If(
|
||||
nodx.If(
|
||||
execution.FinishedAt.Valid,
|
||||
html.Tr(
|
||||
html.Th(component.SpanText("Took")),
|
||||
html.Td(component.SpanText(
|
||||
nodx.Tr(
|
||||
nodx.Th(component.SpanText("Took")),
|
||||
nodx.Td(component.SpanText(
|
||||
execution.FinishedAt.Time.Sub(execution.StartedAt).String(),
|
||||
)),
|
||||
),
|
||||
),
|
||||
gomponents.If(
|
||||
nodx.If(
|
||||
execution.DeletedAt.Valid,
|
||||
html.Tr(
|
||||
html.Th(component.SpanText("Deleted at")),
|
||||
html.Td(component.SpanText(
|
||||
nodx.Tr(
|
||||
nodx.Th(component.SpanText("Deleted at")),
|
||||
nodx.Td(component.SpanText(
|
||||
execution.DeletedAt.Time.Local().Format(timeutil.LayoutYYYYMMDDHHMMSSPretty),
|
||||
)),
|
||||
),
|
||||
),
|
||||
gomponents.If(
|
||||
nodx.If(
|
||||
execution.FileSize.Valid,
|
||||
html.Tr(
|
||||
html.Th(component.SpanText("File size")),
|
||||
html.Td(component.PrettyFileSize(execution.FileSize)),
|
||||
nodx.Tr(
|
||||
nodx.Th(component.SpanText("File size")),
|
||||
nodx.Td(component.PrettyFileSize(execution.FileSize)),
|
||||
),
|
||||
),
|
||||
),
|
||||
gomponents.If(
|
||||
nodx.If(
|
||||
execution.Status == "success",
|
||||
html.Div(
|
||||
html.Class("flex justify-end items-center space-x-2"),
|
||||
nodx.Div(
|
||||
nodx.Class("flex justify-end items-center space-x-2"),
|
||||
deleteExecutionButton(execution.ID),
|
||||
html.A(
|
||||
html.Href("/dashboard/executions/"+execution.ID.String()+"/download"),
|
||||
html.Target("_blank"),
|
||||
html.Class("btn btn-primary"),
|
||||
nodx.A(
|
||||
nodx.Href("/dashboard/executions/"+execution.ID.String()+"/download"),
|
||||
nodx.Target("_blank"),
|
||||
nodx.Class("btn btn-primary"),
|
||||
component.SpanText("Download"),
|
||||
lucide.Download(),
|
||||
),
|
||||
@@ -134,7 +133,7 @@ func showExecutionButton(
|
||||
},
|
||||
})
|
||||
|
||||
return html.Div(
|
||||
return nodx.Div(
|
||||
mo.HTML,
|
||||
component.OptionsDropdownButton(
|
||||
mo.OpenerAttr,
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
package executions
|
||||
|
||||
import (
|
||||
lucide "github.com/eduardolat/gomponents-lucide"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/component"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/htmx"
|
||||
"github.com/google/uuid"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
lucide "github.com/nodxdev/nodxgo-lucide"
|
||||
)
|
||||
|
||||
func (h *handlers) deleteExecutionHandler(c echo.Context) error {
|
||||
@@ -26,12 +25,12 @@ func (h *handlers) deleteExecutionHandler(c echo.Context) error {
|
||||
return htmx.RespondRefresh(c)
|
||||
}
|
||||
|
||||
func deleteExecutionButton(executionID uuid.UUID) gomponents.Node {
|
||||
return html.Button(
|
||||
func deleteExecutionButton(executionID uuid.UUID) nodx.Node {
|
||||
return nodx.Button(
|
||||
htmx.HxDelete("/dashboard/executions/"+executionID.String()),
|
||||
htmx.HxDisabledELT("this"),
|
||||
htmx.HxConfirm("Are you sure you want to delete this execution? It will delete the backup file from the destination and it can't be recovered."),
|
||||
html.Class("btn btn-error btn-outline"),
|
||||
nodx.Class("btn btn-error btn-outline"),
|
||||
component.SpanText("Delete"),
|
||||
lucide.Trash(),
|
||||
)
|
||||
|
||||
@@ -10,8 +10,7 @@ import (
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/component"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/htmx"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
)
|
||||
|
||||
func healthButtonHandler(servs *service.Service) echo.HandlerFunc {
|
||||
@@ -27,7 +26,7 @@ func healthButtonHandler(servs *service.Service) echo.HandlerFunc {
|
||||
return htmx.RespondToastError(c, err.Error())
|
||||
}
|
||||
|
||||
return echoutil.RenderGomponent(c, http.StatusOK, healthButton(
|
||||
return echoutil.RenderNodx(c, http.StatusOK, healthButton(
|
||||
databasesQty, destinationsQty,
|
||||
))
|
||||
}
|
||||
@@ -36,7 +35,7 @@ func healthButtonHandler(servs *service.Service) echo.HandlerFunc {
|
||||
func healthButton(
|
||||
databasesQty dbgen.DatabasesServiceGetDatabasesQtyRow,
|
||||
destinationsQty dbgen.DestinationsServiceGetDestinationsQtyRow,
|
||||
) gomponents.Node {
|
||||
) nodx.Node {
|
||||
isHealthy := true
|
||||
|
||||
if databasesQty.Unhealthy > 0 {
|
||||
@@ -54,7 +53,7 @@ func healthButton(
|
||||
mo := component.Modal(component.ModalParams{
|
||||
Size: component.SizeMd,
|
||||
Title: "Health status",
|
||||
Content: []gomponents.Node{
|
||||
Content: []nodx.Node{
|
||||
component.PText(`
|
||||
The health check for both databases and destinations runs automatically
|
||||
every 10 minutes, when PG Back Web starts, and when you click the
|
||||
@@ -62,40 +61,40 @@ func healthButton(
|
||||
information and error messages by clicking the health check button
|
||||
for each resource.
|
||||
`),
|
||||
html.Table(
|
||||
html.Class("table mt-2"),
|
||||
html.THead(
|
||||
html.Tr(
|
||||
html.Th(component.SpanText("Resource")),
|
||||
html.Th(component.SpanText("Total")),
|
||||
html.Th(component.SpanText("Healthy")),
|
||||
html.Th(component.SpanText("Unhealthy")),
|
||||
nodx.Table(
|
||||
nodx.Class("table mt-2"),
|
||||
nodx.Thead(
|
||||
nodx.Tr(
|
||||
nodx.Th(component.SpanText("Resource")),
|
||||
nodx.Th(component.SpanText("Total")),
|
||||
nodx.Th(component.SpanText("Healthy")),
|
||||
nodx.Th(component.SpanText("Unhealthy")),
|
||||
),
|
||||
),
|
||||
html.TBody(
|
||||
html.Tr(
|
||||
html.Td(component.SpanText("Databases")),
|
||||
html.Td(component.SpanText(fmt.Sprintf("%d", databasesQty.All))),
|
||||
html.Td(component.SpanText(fmt.Sprintf("%d", databasesQty.Healthy))),
|
||||
html.Td(component.SpanText(fmt.Sprintf("%d", databasesQty.Unhealthy))),
|
||||
nodx.Tbody(
|
||||
nodx.Tr(
|
||||
nodx.Td(component.SpanText("Databases")),
|
||||
nodx.Td(component.SpanText(fmt.Sprintf("%d", databasesQty.All))),
|
||||
nodx.Td(component.SpanText(fmt.Sprintf("%d", databasesQty.Healthy))),
|
||||
nodx.Td(component.SpanText(fmt.Sprintf("%d", databasesQty.Unhealthy))),
|
||||
),
|
||||
html.Tr(
|
||||
html.Td(component.SpanText("Destinations")),
|
||||
html.Td(component.SpanText(fmt.Sprintf("%d", destinationsQty.All))),
|
||||
html.Td(component.SpanText(fmt.Sprintf("%d", destinationsQty.Healthy))),
|
||||
html.Td(component.SpanText(fmt.Sprintf("%d", destinationsQty.Unhealthy))),
|
||||
nodx.Tr(
|
||||
nodx.Td(component.SpanText("Destinations")),
|
||||
nodx.Td(component.SpanText(fmt.Sprintf("%d", destinationsQty.All))),
|
||||
nodx.Td(component.SpanText(fmt.Sprintf("%d", destinationsQty.Healthy))),
|
||||
nodx.Td(component.SpanText(fmt.Sprintf("%d", destinationsQty.Unhealthy))),
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
})
|
||||
|
||||
return html.Div(
|
||||
html.Class("inline-block"),
|
||||
return nodx.Div(
|
||||
nodx.Class("inline-block"),
|
||||
mo.HTML,
|
||||
html.Button(
|
||||
nodx.Button(
|
||||
mo.OpenerAttr,
|
||||
html.Class("btn btn-ghost btn-neutral"),
|
||||
nodx.Class("btn btn-ghost btn-neutral"),
|
||||
component.SpanText("Health status"),
|
||||
component.Ping(pingColor),
|
||||
),
|
||||
|
||||
@@ -1,52 +1,51 @@
|
||||
package profile
|
||||
|
||||
import (
|
||||
lucide "github.com/eduardolat/gomponents-lucide"
|
||||
"github.com/eduardolat/pgbackweb/internal/database/dbgen"
|
||||
"github.com/eduardolat/pgbackweb/internal/util/timeutil"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/component"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/htmx"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
lucide "github.com/nodxdev/nodxgo-lucide"
|
||||
)
|
||||
|
||||
func closeAllSessionsForm(sessions []dbgen.Session) gomponents.Node {
|
||||
func closeAllSessionsForm(sessions []dbgen.Session) nodx.Node {
|
||||
return component.CardBox(component.CardBoxParams{
|
||||
Children: []gomponents.Node{
|
||||
Children: []nodx.Node{
|
||||
component.H2Text("Close all sessions"),
|
||||
component.PText("This will log you out from all devices including this one."),
|
||||
html.Button(
|
||||
nodx.Button(
|
||||
htmx.HxPost("/auth/logout-all"),
|
||||
htmx.HxDisabledELT("this"),
|
||||
htmx.HxConfirm("Are you sure you want to close all your sessions?"),
|
||||
html.Class("mt-2 btn btn-error"),
|
||||
nodx.Class("mt-2 btn btn-error"),
|
||||
component.SpanText("Close all sessions"),
|
||||
lucide.LogOut(),
|
||||
),
|
||||
|
||||
html.Div(html.Class("divider")),
|
||||
nodx.Div(nodx.Class("divider")),
|
||||
|
||||
component.H2Text("Active sessions"),
|
||||
component.PText("All sessions are open for a maximum of 12 hours."),
|
||||
html.Div(
|
||||
html.Class("overflow-x-auto"),
|
||||
html.Table(
|
||||
html.Class("table"),
|
||||
html.THead(
|
||||
html.Tr(
|
||||
html.Th(component.SpanText("Login time")),
|
||||
html.Th(component.SpanText("IP address")),
|
||||
html.Th(component.SpanText("User agent")),
|
||||
nodx.Div(
|
||||
nodx.Class("overflow-x-auto"),
|
||||
nodx.Table(
|
||||
nodx.Class("table"),
|
||||
nodx.Thead(
|
||||
nodx.Tr(
|
||||
nodx.Th(component.SpanText("Login time")),
|
||||
nodx.Th(component.SpanText("IP address")),
|
||||
nodx.Th(component.SpanText("User agent")),
|
||||
),
|
||||
),
|
||||
html.TBody(
|
||||
component.GMap(sessions, func(session dbgen.Session) gomponents.Node {
|
||||
return html.Tr(
|
||||
html.Td(component.SpanText(
|
||||
nodx.Tbody(
|
||||
nodx.Map(sessions, func(session dbgen.Session) nodx.Node {
|
||||
return nodx.Tr(
|
||||
nodx.Td(component.SpanText(
|
||||
session.CreatedAt.Local().Format(timeutil.LayoutYYYYMMDDHHMMSSPretty),
|
||||
)),
|
||||
html.Td(component.SpanText(session.Ip)),
|
||||
html.Td(component.SpanText(session.UserAgent)),
|
||||
nodx.Td(component.SpanText(session.Ip)),
|
||||
nodx.Td(component.SpanText(session.UserAgent)),
|
||||
)
|
||||
}),
|
||||
),
|
||||
|
||||
@@ -10,8 +10,7 @@ import (
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/component"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/layout"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
)
|
||||
|
||||
func (h *handlers) indexPageHandler(c echo.Context) error {
|
||||
@@ -24,19 +23,19 @@ func (h *handlers) indexPageHandler(c echo.Context) error {
|
||||
return c.String(http.StatusInternalServerError, "failed to get user sessions")
|
||||
}
|
||||
|
||||
return echoutil.RenderGomponent(
|
||||
return echoutil.RenderNodx(
|
||||
c, http.StatusOK, indexPage(reqCtx, sessions),
|
||||
)
|
||||
}
|
||||
|
||||
func indexPage(reqCtx reqctx.Ctx, sessions []dbgen.Session) gomponents.Node {
|
||||
content := []gomponents.Node{
|
||||
func indexPage(reqCtx reqctx.Ctx, sessions []dbgen.Session) nodx.Node {
|
||||
content := []nodx.Node{
|
||||
component.H1Text("Profile"),
|
||||
|
||||
html.Div(
|
||||
html.Class("mt-4 grid grid-cols-2 gap-4"),
|
||||
html.Div(updateUserForm(reqCtx.User)),
|
||||
html.Div(closeAllSessionsForm(sessions)),
|
||||
nodx.Div(
|
||||
nodx.Class("mt-4 grid grid-cols-2 gap-4"),
|
||||
nodx.Div(updateUserForm(reqCtx.User)),
|
||||
nodx.Div(closeAllSessionsForm(sessions)),
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
@@ -3,15 +3,14 @@ package profile
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
lucide "github.com/eduardolat/gomponents-lucide"
|
||||
"github.com/eduardolat/pgbackweb/internal/database/dbgen"
|
||||
"github.com/eduardolat/pgbackweb/internal/validate"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/reqctx"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/component"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/htmx"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
lucide "github.com/nodxdev/nodxgo-lucide"
|
||||
)
|
||||
|
||||
func (h *handlers) updateUserHandler(c echo.Context) error {
|
||||
@@ -44,13 +43,13 @@ func (h *handlers) updateUserHandler(c echo.Context) error {
|
||||
return htmx.RespondToastSuccess(c, "Profile updated")
|
||||
}
|
||||
|
||||
func updateUserForm(user dbgen.User) gomponents.Node {
|
||||
func updateUserForm(user dbgen.User) nodx.Node {
|
||||
return component.CardBox(component.CardBoxParams{
|
||||
Children: []gomponents.Node{
|
||||
html.Form(
|
||||
Children: []nodx.Node{
|
||||
nodx.FormEl(
|
||||
htmx.HxPost("/dashboard/profile"),
|
||||
htmx.HxDisabledELT("find button"),
|
||||
html.Class("space-y-2"),
|
||||
nodx.Class("space-y-2"),
|
||||
|
||||
component.H2Text("Update profile"),
|
||||
|
||||
@@ -61,8 +60,8 @@ func updateUserForm(user dbgen.User) gomponents.Node {
|
||||
Required: true,
|
||||
Type: component.InputTypeText,
|
||||
AutoComplete: "name",
|
||||
Children: []gomponents.Node{
|
||||
html.Value(user.Name),
|
||||
Children: []nodx.Node{
|
||||
nodx.Value(user.Name),
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -73,8 +72,8 @@ func updateUserForm(user dbgen.User) gomponents.Node {
|
||||
Required: true,
|
||||
AutoComplete: "email",
|
||||
Type: component.InputTypeEmail,
|
||||
Children: []gomponents.Node{
|
||||
html.Value(user.Email),
|
||||
Children: []nodx.Node{
|
||||
nodx.Value(user.Email),
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -95,12 +94,12 @@ func updateUserForm(user dbgen.User) gomponents.Node {
|
||||
Type: component.InputTypePassword,
|
||||
}),
|
||||
|
||||
html.Div(
|
||||
html.Class("flex justify-end items-center space-x-2 pt-2"),
|
||||
nodx.Div(
|
||||
nodx.Class("flex justify-end items-center space-x-2 pt-2"),
|
||||
component.HxLoadingMd(),
|
||||
html.Button(
|
||||
html.Class("btn btn-primary"),
|
||||
html.Type("submit"),
|
||||
nodx.Button(
|
||||
nodx.Class("btn btn-primary"),
|
||||
nodx.Type("submit"),
|
||||
component.SpanText("Save changes"),
|
||||
lucide.Save(),
|
||||
),
|
||||
|
||||
@@ -12,8 +12,7 @@ import (
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/layout"
|
||||
"github.com/google/uuid"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
)
|
||||
|
||||
type resQueryData struct {
|
||||
@@ -32,32 +31,32 @@ func (h *handlers) indexPageHandler(c echo.Context) error {
|
||||
return c.String(http.StatusBadRequest, err.Error())
|
||||
}
|
||||
|
||||
return echoutil.RenderGomponent(c, http.StatusOK, indexPage(reqCtx, queryData))
|
||||
return echoutil.RenderNodx(c, http.StatusOK, indexPage(reqCtx, queryData))
|
||||
}
|
||||
|
||||
func indexPage(reqCtx reqctx.Ctx, queryData resQueryData) gomponents.Node {
|
||||
content := []gomponents.Node{
|
||||
func indexPage(reqCtx reqctx.Ctx, queryData resQueryData) nodx.Node {
|
||||
content := []nodx.Node{
|
||||
component.H1Text("Restorations"),
|
||||
component.CardBox(component.CardBoxParams{
|
||||
Class: "mt-4",
|
||||
Children: []gomponents.Node{
|
||||
html.Div(
|
||||
html.Class("overflow-x-auto"),
|
||||
html.Table(
|
||||
html.Class("table text-nowrap"),
|
||||
html.THead(
|
||||
html.Tr(
|
||||
html.Th(html.Class("w-1")),
|
||||
html.Th(component.SpanText("Status")),
|
||||
html.Th(component.SpanText("Backup")),
|
||||
html.Th(component.SpanText("Database")),
|
||||
html.Th(component.SpanText("Execution")),
|
||||
html.Th(component.SpanText("Started at")),
|
||||
html.Th(component.SpanText("Finished at")),
|
||||
html.Th(component.SpanText("Duration")),
|
||||
Children: []nodx.Node{
|
||||
nodx.Div(
|
||||
nodx.Class("overflow-x-auto"),
|
||||
nodx.Table(
|
||||
nodx.Class("table text-nowrap"),
|
||||
nodx.Thead(
|
||||
nodx.Tr(
|
||||
nodx.Th(nodx.Class("w-1")),
|
||||
nodx.Th(component.SpanText("Status")),
|
||||
nodx.Th(component.SpanText("Backup")),
|
||||
nodx.Th(component.SpanText("Database")),
|
||||
nodx.Th(component.SpanText("Execution")),
|
||||
nodx.Th(component.SpanText("Started at")),
|
||||
nodx.Th(component.SpanText("Finished at")),
|
||||
nodx.Th(component.SpanText("Duration")),
|
||||
),
|
||||
),
|
||||
html.TBody(
|
||||
nodx.Tbody(
|
||||
component.SkeletonTr(8),
|
||||
htmx.HxGet(func() string {
|
||||
url := "/dashboard/restorations/list?page=1"
|
||||
|
||||
@@ -15,8 +15,7 @@ import (
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/htmx"
|
||||
"github.com/google/uuid"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
)
|
||||
|
||||
type listResQueryData struct {
|
||||
@@ -52,7 +51,7 @@ func (h *handlers) listRestorationsHandler(c echo.Context) error {
|
||||
return htmx.RespondToastError(c, err.Error())
|
||||
}
|
||||
|
||||
return echoutil.RenderGomponent(
|
||||
return echoutil.RenderNodx(
|
||||
c, http.StatusOK, listRestorations(queryData, pagination, restorations),
|
||||
)
|
||||
}
|
||||
@@ -61,7 +60,7 @@ func listRestorations(
|
||||
queryData listResQueryData,
|
||||
pagination paginateutil.PaginateResponse,
|
||||
restorations []dbgen.RestorationsServicePaginateRestorationsRow,
|
||||
) gomponents.Node {
|
||||
) nodx.Node {
|
||||
if len(restorations) < 1 {
|
||||
return component.EmptyResultsTr(component.EmptyResultsParams{
|
||||
Title: "No restorations found",
|
||||
@@ -69,34 +68,34 @@ func listRestorations(
|
||||
})
|
||||
}
|
||||
|
||||
trs := []gomponents.Node{}
|
||||
trs := []nodx.Node{}
|
||||
for _, restoration := range restorations {
|
||||
trs = append(trs, html.Tr(
|
||||
html.Td(
|
||||
trs = append(trs, nodx.Tr(
|
||||
nodx.Td(
|
||||
showRestorationButton(restoration),
|
||||
),
|
||||
html.Td(component.StatusBadge(restoration.Status)),
|
||||
html.Td(component.SpanText(restoration.BackupName)),
|
||||
html.Td(component.SpanText(func() string {
|
||||
nodx.Td(component.StatusBadge(restoration.Status)),
|
||||
nodx.Td(component.SpanText(restoration.BackupName)),
|
||||
nodx.Td(component.SpanText(func() string {
|
||||
if restoration.DatabaseName.Valid {
|
||||
return restoration.DatabaseName.String
|
||||
}
|
||||
return "Other database"
|
||||
}())),
|
||||
html.Td(component.SpanText(restoration.ExecutionID.String())),
|
||||
html.Td(component.SpanText(
|
||||
nodx.Td(component.SpanText(restoration.ExecutionID.String())),
|
||||
nodx.Td(component.SpanText(
|
||||
restoration.StartedAt.Local().Format(timeutil.LayoutYYYYMMDDHHMMSSPretty),
|
||||
)),
|
||||
html.Td(
|
||||
gomponents.If(
|
||||
nodx.Td(
|
||||
nodx.If(
|
||||
restoration.FinishedAt.Valid,
|
||||
component.SpanText(
|
||||
restoration.FinishedAt.Time.Local().Format(timeutil.LayoutYYYYMMDDHHMMSSPretty),
|
||||
),
|
||||
),
|
||||
),
|
||||
html.Td(
|
||||
gomponents.If(
|
||||
nodx.Td(
|
||||
nodx.If(
|
||||
restoration.FinishedAt.Valid,
|
||||
component.SpanText(
|
||||
restoration.FinishedAt.Time.Sub(restoration.StartedAt).String(),
|
||||
@@ -107,7 +106,7 @@ func listRestorations(
|
||||
}
|
||||
|
||||
if pagination.HasNextPage {
|
||||
trs = append(trs, html.Tr(
|
||||
trs = append(trs, nodx.Tr(
|
||||
htmx.HxGet(func() string {
|
||||
url := "/dashboard/restorations/list"
|
||||
url = strutil.AddQueryParamToUrl(url, "page", fmt.Sprintf("%d", pagination.NextPage))
|
||||
|
||||
@@ -1,76 +1,75 @@
|
||||
package restorations
|
||||
|
||||
import (
|
||||
lucide "github.com/eduardolat/gomponents-lucide"
|
||||
"github.com/eduardolat/pgbackweb/internal/database/dbgen"
|
||||
"github.com/eduardolat/pgbackweb/internal/util/timeutil"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/component"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
lucide "github.com/nodxdev/nodxgo-lucide"
|
||||
)
|
||||
|
||||
func showRestorationButton(
|
||||
restoration dbgen.RestorationsServicePaginateRestorationsRow,
|
||||
) gomponents.Node {
|
||||
) nodx.Node {
|
||||
mo := component.Modal(component.ModalParams{
|
||||
Title: "Restoration details",
|
||||
Size: component.SizeMd,
|
||||
Content: []gomponents.Node{
|
||||
html.Div(
|
||||
html.Class("overflow-x-auto"),
|
||||
html.Table(
|
||||
html.Class("table [&_th]:text-nowrap"),
|
||||
html.Tr(
|
||||
html.Th(component.SpanText("ID")),
|
||||
html.Td(component.SpanText(restoration.ID.String())),
|
||||
Content: []nodx.Node{
|
||||
nodx.Div(
|
||||
nodx.Class("overflow-x-auto"),
|
||||
nodx.Table(
|
||||
nodx.Class("table [&_th]:text-nowrap"),
|
||||
nodx.Tr(
|
||||
nodx.Th(component.SpanText("ID")),
|
||||
nodx.Td(component.SpanText(restoration.ID.String())),
|
||||
),
|
||||
html.Tr(
|
||||
html.Th(component.SpanText("Status")),
|
||||
html.Td(component.StatusBadge(restoration.Status)),
|
||||
nodx.Tr(
|
||||
nodx.Th(component.SpanText("Status")),
|
||||
nodx.Td(component.StatusBadge(restoration.Status)),
|
||||
),
|
||||
html.Tr(
|
||||
html.Th(component.SpanText("Backup")),
|
||||
html.Td(component.SpanText(restoration.BackupName)),
|
||||
nodx.Tr(
|
||||
nodx.Th(component.SpanText("Backup")),
|
||||
nodx.Td(component.SpanText(restoration.BackupName)),
|
||||
),
|
||||
html.Tr(
|
||||
html.Th(component.SpanText("Database")),
|
||||
html.Td(component.SpanText(func() string {
|
||||
nodx.Tr(
|
||||
nodx.Th(component.SpanText("Database")),
|
||||
nodx.Td(component.SpanText(func() string {
|
||||
if restoration.DatabaseName.Valid {
|
||||
return restoration.DatabaseName.String
|
||||
}
|
||||
return "Other database"
|
||||
}())),
|
||||
),
|
||||
gomponents.If(
|
||||
nodx.If(
|
||||
restoration.Message.Valid,
|
||||
html.Tr(
|
||||
html.Th(component.SpanText("Message")),
|
||||
html.Td(
|
||||
html.Class("break-all"),
|
||||
nodx.Tr(
|
||||
nodx.Th(component.SpanText("Message")),
|
||||
nodx.Td(
|
||||
nodx.Class("break-all"),
|
||||
component.SpanText(restoration.Message.String),
|
||||
),
|
||||
),
|
||||
),
|
||||
html.Tr(
|
||||
html.Th(component.SpanText("Started At")),
|
||||
html.Td(component.SpanText(
|
||||
nodx.Tr(
|
||||
nodx.Th(component.SpanText("Started At")),
|
||||
nodx.Td(component.SpanText(
|
||||
restoration.StartedAt.Local().Format(timeutil.LayoutYYYYMMDDHHMMSSPretty),
|
||||
)),
|
||||
),
|
||||
gomponents.If(
|
||||
nodx.If(
|
||||
restoration.FinishedAt.Valid,
|
||||
html.Tr(
|
||||
html.Th(component.SpanText("Finished At")),
|
||||
html.Td(component.SpanText(
|
||||
nodx.Tr(
|
||||
nodx.Th(component.SpanText("Finished At")),
|
||||
nodx.Td(component.SpanText(
|
||||
restoration.FinishedAt.Time.Local().Format(timeutil.LayoutYYYYMMDDHHMMSSPretty),
|
||||
)),
|
||||
),
|
||||
),
|
||||
gomponents.If(
|
||||
nodx.If(
|
||||
restoration.FinishedAt.Valid,
|
||||
html.Tr(
|
||||
html.Th(component.SpanText("Took")),
|
||||
html.Td(component.SpanText(
|
||||
nodx.Tr(
|
||||
nodx.Th(component.SpanText("Took")),
|
||||
nodx.Td(component.SpanText(
|
||||
restoration.FinishedAt.Time.Sub(restoration.StartedAt).String(),
|
||||
)),
|
||||
),
|
||||
@@ -80,15 +79,15 @@ func showRestorationButton(
|
||||
},
|
||||
})
|
||||
|
||||
button := html.Button(
|
||||
button := nodx.Button(
|
||||
mo.OpenerAttr,
|
||||
html.Class("btn btn-square btn-sm btn-ghost"),
|
||||
nodx.Class("btn btn-square btn-sm btn-ghost"),
|
||||
lucide.Eye(),
|
||||
)
|
||||
|
||||
return html.Div(
|
||||
html.Class("inline-block tooltip tooltip-right"),
|
||||
html.Data("tip", "Show details"),
|
||||
return nodx.Div(
|
||||
nodx.Class("inline-block tooltip tooltip-right"),
|
||||
nodx.Data("tip", "Show details"),
|
||||
mo.HTML,
|
||||
button,
|
||||
)
|
||||
|
||||
@@ -11,8 +11,7 @@ import (
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/layout"
|
||||
"github.com/google/uuid"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
)
|
||||
|
||||
func (h *handlers) indexPageHandler(c echo.Context) error {
|
||||
@@ -40,7 +39,7 @@ func (h *handlers) indexPageHandler(c echo.Context) error {
|
||||
return c.String(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
return echoutil.RenderGomponent(
|
||||
return echoutil.RenderNodx(
|
||||
c, http.StatusOK,
|
||||
indexPage(
|
||||
reqCtx, databasesQty, destinationsQty, backupsQty, executionsQty,
|
||||
@@ -56,7 +55,7 @@ func indexPage(
|
||||
backupsQty dbgen.BackupsServiceGetBackupsQtyRow,
|
||||
executionsQty dbgen.ExecutionsServiceGetExecutionsQtyRow,
|
||||
restorationsQty dbgen.RestorationsServiceGetRestorationsQtyRow,
|
||||
) gomponents.Node {
|
||||
) nodx.Node {
|
||||
type ChartData struct {
|
||||
Label string
|
||||
Labels []string
|
||||
@@ -68,13 +67,13 @@ func indexPage(
|
||||
title string,
|
||||
count int64,
|
||||
chartData ChartData,
|
||||
) gomponents.Node {
|
||||
chart := func() gomponents.Node {
|
||||
notAvailable := html.Div(
|
||||
html.Class("size-[218px] flex flex-col justify-center items-center"),
|
||||
html.Span(
|
||||
html.Class("text-sm text-base-content pb-[32px]"),
|
||||
gomponents.Text("Chart waiting for data"),
|
||||
) nodx.Node {
|
||||
chart := func() nodx.Node {
|
||||
notAvailable := nodx.Div(
|
||||
nodx.Class("size-[218px] flex flex-col justify-center items-center"),
|
||||
nodx.SpanEl(
|
||||
nodx.Class("text-sm text-base-content pb-[32px]"),
|
||||
nodx.Text("Chart waiting for data"),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -110,10 +109,10 @@ func indexPage(
|
||||
bgColors += fmt.Sprintf("'%s',", color)
|
||||
}
|
||||
|
||||
return html.Div(
|
||||
html.Class("mt-2"),
|
||||
html.Div(html.Canvas(html.ID(chartID))),
|
||||
html.Script(gomponents.Raw(`
|
||||
return nodx.Div(
|
||||
nodx.Class("mt-2"),
|
||||
nodx.Div(nodx.Canvas(nodx.Id(chartID))),
|
||||
nodx.Script(nodx.Raw(`
|
||||
new Chart(document.getElementById('`+chartID+`'), {
|
||||
type: 'doughnut',
|
||||
data: {
|
||||
@@ -140,7 +139,7 @@ func indexPage(
|
||||
|
||||
return component.CardBox(component.CardBoxParams{
|
||||
Class: "flex-none text-center w-[250px]",
|
||||
Children: []gomponents.Node{
|
||||
Children: []nodx.Node{
|
||||
component.H2Text(fmt.Sprintf("%d %s", count, title)),
|
||||
chart(),
|
||||
},
|
||||
@@ -154,13 +153,13 @@ func indexPage(
|
||||
blueColor = "#00b6ff"
|
||||
)
|
||||
|
||||
content := []gomponents.Node{
|
||||
html.Div(
|
||||
html.Class("flex justify-center"),
|
||||
content := []nodx.Node{
|
||||
nodx.Div(
|
||||
nodx.Class("flex justify-center"),
|
||||
component.H1Text("Summary"),
|
||||
),
|
||||
html.Div(
|
||||
html.Class("mt-4 flex justify-center flex-wrap gap-4"),
|
||||
nodx.Div(
|
||||
nodx.Class("mt-4 flex justify-center flex-wrap gap-4"),
|
||||
|
||||
countCard("Databases", databasesQty.All, ChartData{
|
||||
Label: "Quantity",
|
||||
|
||||
@@ -1,52 +1,51 @@
|
||||
package summary
|
||||
|
||||
import (
|
||||
lucide "github.com/eduardolat/gomponents-lucide"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/alpine"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/component"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
lucide "github.com/nodxdev/nodxgo-lucide"
|
||||
)
|
||||
|
||||
func indexHowTo() gomponents.Node {
|
||||
return html.Div(
|
||||
func indexHowTo() nodx.Node {
|
||||
return nodx.Div(
|
||||
alpine.XData("alpineSummaryHowToSlider()"),
|
||||
alpine.XCloak(),
|
||||
html.Class("mt-6 flex flex-col justify-center items-center mx-auto"),
|
||||
nodx.Class("mt-6 flex flex-col justify-center items-center mx-auto"),
|
||||
|
||||
component.H2Text("How to use PG Back Web"),
|
||||
|
||||
component.CardBox(component.CardBoxParams{
|
||||
Class: "mt-4 space-y-4 max-w-[600px]",
|
||||
Children: []gomponents.Node{
|
||||
html.Div(
|
||||
html.Class("flex justify-center"),
|
||||
html.Ul(
|
||||
html.Class("steps"),
|
||||
html.Li(
|
||||
html.Class("step"),
|
||||
Children: []nodx.Node{
|
||||
nodx.Div(
|
||||
nodx.Class("flex justify-center"),
|
||||
nodx.Ul(
|
||||
nodx.Class("steps"),
|
||||
nodx.Li(
|
||||
nodx.Class("step"),
|
||||
alpine.XBind("class", "currentSlide >= 1 ? 'step-primary' : ''"),
|
||||
gomponents.Text("Create database"),
|
||||
nodx.Text("Create database"),
|
||||
),
|
||||
html.Li(
|
||||
html.Class("step"),
|
||||
nodx.Li(
|
||||
nodx.Class("step"),
|
||||
alpine.XBind("class", "currentSlide >= 2 ? 'step-primary' : ''"),
|
||||
gomponents.Text("Create destination"),
|
||||
nodx.Text("Create destination"),
|
||||
),
|
||||
html.Li(
|
||||
html.Class("step"),
|
||||
nodx.Li(
|
||||
nodx.Class("step"),
|
||||
alpine.XBind("class", "currentSlide >= 3 ? 'step-primary' : ''"),
|
||||
gomponents.Text("Create backup"),
|
||||
nodx.Text("Create backup"),
|
||||
),
|
||||
html.Li(
|
||||
html.Class("step"),
|
||||
nodx.Li(
|
||||
nodx.Class("step"),
|
||||
alpine.XBind("class", "currentSlide >= 4 ? 'step-primary' : ''"),
|
||||
gomponents.Text("Wait for executions"),
|
||||
nodx.Text("Wait for executions"),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
html.Div(
|
||||
nodx.Div(
|
||||
alpine.XShow("currentSlide === 1"),
|
||||
component.H3Text("Create database"),
|
||||
component.PText(`
|
||||
@@ -57,7 +56,7 @@ func indexHowTo() gomponents.Node {
|
||||
`),
|
||||
),
|
||||
|
||||
html.Div(
|
||||
nodx.Div(
|
||||
alpine.XShow("currentSlide === 2"),
|
||||
component.H3Text("Create S3 destination (optional)"),
|
||||
component.PText(`
|
||||
@@ -70,7 +69,7 @@ func indexHowTo() gomponents.Node {
|
||||
`),
|
||||
),
|
||||
|
||||
html.Div(
|
||||
nodx.Div(
|
||||
alpine.XShow("currentSlide === 3"),
|
||||
component.H3Text("Create backup"),
|
||||
component.PText(`
|
||||
@@ -82,7 +81,7 @@ func indexHowTo() gomponents.Node {
|
||||
`),
|
||||
),
|
||||
|
||||
html.Div(
|
||||
nodx.Div(
|
||||
alpine.XShow("currentSlide === 4"),
|
||||
component.H3Text("Wait for executions"),
|
||||
component.PText(`
|
||||
@@ -96,19 +95,19 @@ func indexHowTo() gomponents.Node {
|
||||
`),
|
||||
),
|
||||
|
||||
html.Div(
|
||||
html.Class("mt-4 flex justify-between"),
|
||||
html.Button(
|
||||
nodx.Div(
|
||||
nodx.Class("mt-4 flex justify-between"),
|
||||
nodx.Button(
|
||||
alpine.XBind("disabled", "!hasPrevSlide"),
|
||||
alpine.XOn("click", "prevSlide"),
|
||||
html.Class("btn btn-neutral btn-ghost"),
|
||||
nodx.Class("btn btn-neutral btn-ghost"),
|
||||
lucide.ChevronLeft(),
|
||||
component.SpanText("Previous"),
|
||||
),
|
||||
html.Button(
|
||||
nodx.Button(
|
||||
alpine.XBind("disabled", "!hasNextSlide"),
|
||||
alpine.XOn("click", "nextSlide"),
|
||||
html.Class("btn btn-neutral btn-ghost"),
|
||||
nodx.Class("btn btn-neutral btn-ghost"),
|
||||
component.SpanText("Next"),
|
||||
lucide.ChevronRight(),
|
||||
),
|
||||
|
||||
@@ -9,8 +9,7 @@ import (
|
||||
"github.com/eduardolat/pgbackweb/internal/util/maputil"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/alpine"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/component"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
)
|
||||
|
||||
func createAndUpdateWebhookForm(
|
||||
@@ -18,55 +17,55 @@ func createAndUpdateWebhookForm(
|
||||
destinations []dbgen.DestinationsServiceGetAllDestinationsRow,
|
||||
backups []dbgen.Backup,
|
||||
webhook ...dbgen.Webhook,
|
||||
) gomponents.Node {
|
||||
) nodx.Node {
|
||||
shouldPrefill, pickedWebhook := false, dbgen.Webhook{}
|
||||
if len(webhook) > 0 {
|
||||
shouldPrefill = true
|
||||
pickedWebhook = webhook[0]
|
||||
}
|
||||
|
||||
eventTypeOptions := gomponents.Group([]gomponents.Node{
|
||||
html.Option(
|
||||
gomponents.Text("Select an event type"),
|
||||
html.Disabled(),
|
||||
gomponents.If(
|
||||
eventTypeOptions := nodx.Group(
|
||||
nodx.Option(
|
||||
nodx.Text("Select an event type"),
|
||||
nodx.Disabled(""),
|
||||
nodx.If(
|
||||
!shouldPrefill,
|
||||
html.Selected(),
|
||||
nodx.Selected(""),
|
||||
),
|
||||
),
|
||||
|
||||
component.GMap(
|
||||
nodx.Map(
|
||||
maputil.GetSortedStringKeys(webhooks.FullEventTypes),
|
||||
func(key string) gomponents.Node {
|
||||
func(key string) nodx.Node {
|
||||
val := webhooks.FullEventTypes[key]
|
||||
return html.Option(
|
||||
html.Value(key),
|
||||
gomponents.Text(val),
|
||||
gomponents.If(
|
||||
return nodx.Option(
|
||||
nodx.Value(key),
|
||||
nodx.Text(val),
|
||||
nodx.If(
|
||||
shouldPrefill && key == pickedWebhook.EventType,
|
||||
html.Selected(),
|
||||
nodx.Selected(""),
|
||||
),
|
||||
)
|
||||
},
|
||||
),
|
||||
})
|
||||
)
|
||||
|
||||
databaseSelect := component.SelectControl(component.SelectControlParams{
|
||||
Name: "target_ids",
|
||||
Label: "Database targets",
|
||||
Required: true,
|
||||
Children: []gomponents.Node{
|
||||
Children: []nodx.Node{
|
||||
alpine.XModel("targetIds"),
|
||||
html.Multiple(),
|
||||
component.GMap(
|
||||
nodx.Multiple(""),
|
||||
nodx.Map(
|
||||
databases,
|
||||
func(db dbgen.DatabasesServiceGetAllDatabasesRow) gomponents.Node {
|
||||
return html.Option(
|
||||
html.Value(db.ID.String()),
|
||||
gomponents.Text(db.Name),
|
||||
gomponents.If(
|
||||
func(db dbgen.DatabasesServiceGetAllDatabasesRow) nodx.Node {
|
||||
return nodx.Option(
|
||||
nodx.Value(db.ID.String()),
|
||||
nodx.Text(db.Name),
|
||||
nodx.If(
|
||||
shouldPrefill && slices.Contains(pickedWebhook.TargetIds, db.ID),
|
||||
html.Selected(),
|
||||
nodx.Selected(""),
|
||||
),
|
||||
)
|
||||
},
|
||||
@@ -78,18 +77,18 @@ func createAndUpdateWebhookForm(
|
||||
Name: "target_ids",
|
||||
Label: "Destination targets",
|
||||
Required: true,
|
||||
Children: []gomponents.Node{
|
||||
Children: []nodx.Node{
|
||||
alpine.XModel("targetIds"),
|
||||
html.Multiple(),
|
||||
component.GMap(
|
||||
nodx.Multiple(""),
|
||||
nodx.Map(
|
||||
destinations,
|
||||
func(dest dbgen.DestinationsServiceGetAllDestinationsRow) gomponents.Node {
|
||||
return html.Option(
|
||||
html.Value(dest.ID.String()),
|
||||
gomponents.Text(dest.Name),
|
||||
gomponents.If(
|
||||
func(dest dbgen.DestinationsServiceGetAllDestinationsRow) nodx.Node {
|
||||
return nodx.Option(
|
||||
nodx.Value(dest.ID.String()),
|
||||
nodx.Text(dest.Name),
|
||||
nodx.If(
|
||||
shouldPrefill && slices.Contains(pickedWebhook.TargetIds, dest.ID),
|
||||
html.Selected(),
|
||||
nodx.Selected(""),
|
||||
),
|
||||
)
|
||||
},
|
||||
@@ -101,18 +100,18 @@ func createAndUpdateWebhookForm(
|
||||
Name: "target_ids",
|
||||
Label: "Backup targets",
|
||||
Required: true,
|
||||
Children: []gomponents.Node{
|
||||
Children: []nodx.Node{
|
||||
alpine.XModel("targetIds"),
|
||||
html.Multiple(),
|
||||
component.GMap(
|
||||
nodx.Multiple(""),
|
||||
nodx.Map(
|
||||
backups,
|
||||
func(backup dbgen.Backup) gomponents.Node {
|
||||
return html.Option(
|
||||
html.Value(backup.ID.String()),
|
||||
gomponents.Text(backup.Name),
|
||||
gomponents.If(
|
||||
func(backup dbgen.Backup) nodx.Node {
|
||||
return nodx.Option(
|
||||
nodx.Value(backup.ID.String()),
|
||||
nodx.Text(backup.Name),
|
||||
nodx.If(
|
||||
shouldPrefill && slices.Contains(pickedWebhook.TargetIds, backup.ID),
|
||||
html.Selected(),
|
||||
nodx.Selected(""),
|
||||
),
|
||||
)
|
||||
},
|
||||
@@ -120,7 +119,7 @@ func createAndUpdateWebhookForm(
|
||||
},
|
||||
})
|
||||
|
||||
eventTypeSelects := map[string]gomponents.Node{
|
||||
eventTypeSelects := map[string]nodx.Node{
|
||||
webhooks.EventTypeDatabaseHealthy.Value.Key: databaseSelect,
|
||||
webhooks.EventTypeDatabaseUnhealthy.Value.Key: databaseSelect,
|
||||
webhooks.EventTypeDestinationHealthy.Value.Key: destinationSelect,
|
||||
@@ -129,7 +128,7 @@ func createAndUpdateWebhookForm(
|
||||
webhooks.EventTypeExecutionFailed.Value.Key: backupSelect,
|
||||
}
|
||||
|
||||
targetIdsSelect := []gomponents.Node{}
|
||||
targetIdsSelect := []nodx.Node{}
|
||||
for eventType, selectNode := range eventTypeSelects {
|
||||
targetIdsSelect = append(targetIdsSelect, alpine.Template(
|
||||
alpine.XIf(fmt.Sprintf("isEventType('%s')", eventType)),
|
||||
@@ -144,8 +143,8 @@ func createAndUpdateWebhookForm(
|
||||
}
|
||||
}
|
||||
|
||||
return html.Div(
|
||||
html.Class("space-y-2"),
|
||||
return nodx.Div(
|
||||
nodx.Class("space-y-2"),
|
||||
|
||||
alpine.XData(`{
|
||||
eventType: "`+pickedWebhook.EventType+`",
|
||||
@@ -193,8 +192,8 @@ func createAndUpdateWebhookForm(
|
||||
Placeholder: "My webhook",
|
||||
Required: true,
|
||||
Type: component.InputTypeText,
|
||||
Children: []gomponents.Node{
|
||||
gomponents.If(shouldPrefill, html.Value(pickedWebhook.Name)),
|
||||
Children: []nodx.Node{
|
||||
nodx.If(shouldPrefill, nodx.Value(pickedWebhook.Name)),
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -202,14 +201,14 @@ func createAndUpdateWebhookForm(
|
||||
Name: "event_type",
|
||||
Label: "Event type",
|
||||
Required: true,
|
||||
HelpButtonChildren: []gomponents.Node{
|
||||
HelpButtonChildren: []nodx.Node{
|
||||
component.H3Text("Event types"),
|
||||
component.PText(`
|
||||
These are the event types that can trigger a webhook.
|
||||
`),
|
||||
|
||||
html.Div(
|
||||
html.Class("space-y-2"),
|
||||
nodx.Div(
|
||||
nodx.Class("space-y-2"),
|
||||
|
||||
component.CardBoxSimple(
|
||||
component.H4Text("Database healthy"),
|
||||
@@ -259,27 +258,27 @@ func createAndUpdateWebhookForm(
|
||||
),
|
||||
),
|
||||
},
|
||||
Children: []gomponents.Node{
|
||||
Children: []nodx.Node{
|
||||
alpine.XModel("eventType"),
|
||||
eventTypeOptions,
|
||||
},
|
||||
}),
|
||||
|
||||
html.Div(targetIdsSelect...),
|
||||
nodx.Div(targetIdsSelect...),
|
||||
|
||||
component.SelectControl(component.SelectControlParams{
|
||||
Name: "is_active",
|
||||
Label: "Activate webhook",
|
||||
Required: true,
|
||||
Children: []gomponents.Node{
|
||||
html.Option(
|
||||
html.Value("true"), gomponents.Text("Yes"),
|
||||
gomponents.If(!shouldPrefill, html.Selected()),
|
||||
gomponents.If(shouldPrefill && pickedWebhook.IsActive, html.Selected()),
|
||||
Children: []nodx.Node{
|
||||
nodx.Option(
|
||||
nodx.Value("true"), nodx.Text("Yes"),
|
||||
nodx.If(!shouldPrefill, nodx.Selected("")),
|
||||
nodx.If(shouldPrefill && pickedWebhook.IsActive, nodx.Selected("")),
|
||||
),
|
||||
html.Option(
|
||||
html.Value("false"), gomponents.Text("No"),
|
||||
gomponents.If(shouldPrefill && !pickedWebhook.IsActive, html.Selected()),
|
||||
nodx.Option(
|
||||
nodx.Value("false"), nodx.Text("No"),
|
||||
nodx.If(shouldPrefill && !pickedWebhook.IsActive, nodx.Selected("")),
|
||||
),
|
||||
},
|
||||
}),
|
||||
@@ -290,8 +289,8 @@ func createAndUpdateWebhookForm(
|
||||
Placeholder: "https://example.com/webhook",
|
||||
Required: true,
|
||||
Type: component.InputTypeUrl,
|
||||
Children: []gomponents.Node{
|
||||
gomponents.If(shouldPrefill, html.Value(pickedWebhook.Url)),
|
||||
Children: []nodx.Node{
|
||||
nodx.If(shouldPrefill, nodx.Value(pickedWebhook.Url)),
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -299,22 +298,22 @@ func createAndUpdateWebhookForm(
|
||||
Name: "method",
|
||||
Label: "Method",
|
||||
Required: true,
|
||||
Children: []gomponents.Node{
|
||||
html.Option(
|
||||
html.Value("POST"),
|
||||
gomponents.Text("POST"),
|
||||
gomponents.If(!shouldPrefill, html.Selected()),
|
||||
gomponents.If(
|
||||
Children: []nodx.Node{
|
||||
nodx.Option(
|
||||
nodx.Value("POST"),
|
||||
nodx.Text("POST"),
|
||||
nodx.If(!shouldPrefill, nodx.Selected("")),
|
||||
nodx.If(
|
||||
shouldPrefill && pickedWebhook.Method == "POST",
|
||||
html.Selected(),
|
||||
nodx.Selected(""),
|
||||
),
|
||||
),
|
||||
html.Option(
|
||||
html.Value("GET"),
|
||||
gomponents.Text("GET"),
|
||||
gomponents.If(
|
||||
nodx.Option(
|
||||
nodx.Value("GET"),
|
||||
nodx.Text("GET"),
|
||||
nodx.If(
|
||||
shouldPrefill && pickedWebhook.Method == "GET",
|
||||
html.Selected(),
|
||||
nodx.Selected(""),
|
||||
),
|
||||
),
|
||||
},
|
||||
@@ -325,12 +324,12 @@ func createAndUpdateWebhookForm(
|
||||
Label: "Headers",
|
||||
Placeholder: `{ "Authorization": "Bearer my-token" }`,
|
||||
HelpText: `By default it will send a { "Content-Type": "application/json" } header.`,
|
||||
Children: []gomponents.Node{
|
||||
Children: []nodx.Node{
|
||||
alpine.XRef("headersTextarea"),
|
||||
alpine.XOn("click.outside", "formatHeadersTextarea()"),
|
||||
alpine.XOn("input", "autoGrowHeadersTextarea()"),
|
||||
gomponents.If(
|
||||
shouldPrefill, gomponents.Text(pickedWebhook.Headers.String),
|
||||
nodx.If(
|
||||
shouldPrefill, nodx.Text(pickedWebhook.Headers.String),
|
||||
),
|
||||
},
|
||||
}),
|
||||
@@ -340,12 +339,12 @@ func createAndUpdateWebhookForm(
|
||||
Label: "Body",
|
||||
Placeholder: `{ "key": "value" }`,
|
||||
HelpText: `By default it will send an empty json object {}.`,
|
||||
Children: []gomponents.Node{
|
||||
Children: []nodx.Node{
|
||||
alpine.XRef("bodyTextarea"),
|
||||
alpine.XOn("click.outside", "formatBodyTextarea()"),
|
||||
alpine.XOn("input", "autoGrowBodyTextarea()"),
|
||||
gomponents.If(
|
||||
shouldPrefill, gomponents.Text(pickedWebhook.Body.String),
|
||||
nodx.If(
|
||||
shouldPrefill, nodx.Text(pickedWebhook.Body.String),
|
||||
),
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"database/sql"
|
||||
"net/http"
|
||||
|
||||
lucide "github.com/eduardolat/gomponents-lucide"
|
||||
"github.com/eduardolat/pgbackweb/internal/database/dbgen"
|
||||
"github.com/eduardolat/pgbackweb/internal/util/echoutil"
|
||||
"github.com/eduardolat/pgbackweb/internal/validate"
|
||||
@@ -12,8 +11,8 @@ import (
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/htmx"
|
||||
"github.com/google/uuid"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
lucide "github.com/nodxdev/nodxgo-lucide"
|
||||
)
|
||||
|
||||
type createWebhookDTO struct {
|
||||
@@ -75,7 +74,7 @@ func (h *handlers) createWebhookFormHandler(c echo.Context) error {
|
||||
return htmx.RespondToastError(c, err.Error())
|
||||
}
|
||||
|
||||
return echoutil.RenderGomponent(c, http.StatusOK, createWebhookForm(
|
||||
return echoutil.RenderNodx(c, http.StatusOK, createWebhookForm(
|
||||
databases, destinations, backups,
|
||||
))
|
||||
}
|
||||
@@ -84,20 +83,20 @@ func createWebhookForm(
|
||||
databases []dbgen.DatabasesServiceGetAllDatabasesRow,
|
||||
destinations []dbgen.DestinationsServiceGetAllDestinationsRow,
|
||||
backups []dbgen.Backup,
|
||||
) gomponents.Node {
|
||||
return html.Form(
|
||||
) nodx.Node {
|
||||
return nodx.FormEl(
|
||||
htmx.HxPost("/dashboard/webhooks/create"),
|
||||
htmx.HxDisabledELT("find button[type='submit']"),
|
||||
html.Class("space-y-2"),
|
||||
nodx.Class("space-y-2"),
|
||||
|
||||
createAndUpdateWebhookForm(databases, destinations, backups),
|
||||
|
||||
html.Div(
|
||||
html.Class("flex justify-end items-center space-x-2 pt-2"),
|
||||
nodx.Div(
|
||||
nodx.Class("flex justify-end items-center space-x-2 pt-2"),
|
||||
component.HxLoadingMd(),
|
||||
html.Button(
|
||||
html.Class("btn btn-primary"),
|
||||
html.Type("submit"),
|
||||
nodx.Button(
|
||||
nodx.Class("btn btn-primary"),
|
||||
nodx.Type("submit"),
|
||||
component.SpanText("Save"),
|
||||
lucide.Save(),
|
||||
),
|
||||
@@ -105,30 +104,30 @@ func createWebhookForm(
|
||||
)
|
||||
}
|
||||
|
||||
func createWebhookButton() gomponents.Node {
|
||||
func createWebhookButton() nodx.Node {
|
||||
mo := component.Modal(component.ModalParams{
|
||||
Size: component.SizeLg,
|
||||
Title: "Create webhook",
|
||||
Content: []gomponents.Node{
|
||||
html.Div(
|
||||
Content: []nodx.Node{
|
||||
nodx.Div(
|
||||
htmx.HxGet("/dashboard/webhooks/create"),
|
||||
htmx.HxSwap("outerHTML"),
|
||||
htmx.HxTrigger("intersect once"),
|
||||
html.Class("p-10 flex justify-center"),
|
||||
nodx.Class("p-10 flex justify-center"),
|
||||
component.HxLoadingMd(),
|
||||
),
|
||||
},
|
||||
})
|
||||
|
||||
button := html.Button(
|
||||
button := nodx.Button(
|
||||
mo.OpenerAttr,
|
||||
html.Class("btn btn-primary"),
|
||||
nodx.Class("btn btn-primary"),
|
||||
component.SpanText("Create webhook"),
|
||||
lucide.Plus(),
|
||||
)
|
||||
|
||||
return html.Div(
|
||||
html.Class("inline-block"),
|
||||
return nodx.Div(
|
||||
nodx.Class("inline-block"),
|
||||
mo.HTML,
|
||||
button,
|
||||
)
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package webhooks
|
||||
|
||||
import (
|
||||
lucide "github.com/eduardolat/gomponents-lucide"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/component"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/htmx"
|
||||
"github.com/google/uuid"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/maragudk/gomponents"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
lucide "github.com/nodxdev/nodxgo-lucide"
|
||||
)
|
||||
|
||||
func (h *handlers) deleteWebhookHandler(c echo.Context) error {
|
||||
@@ -24,7 +24,7 @@ func (h *handlers) deleteWebhookHandler(c echo.Context) error {
|
||||
return htmx.RespondRefresh(c)
|
||||
}
|
||||
|
||||
func deleteWebhookButton(webhookID uuid.UUID) gomponents.Node {
|
||||
func deleteWebhookButton(webhookID uuid.UUID) nodx.Node {
|
||||
return component.OptionsDropdownButton(
|
||||
htmx.HxDelete("/dashboard/webhooks/"+webhookID.String()),
|
||||
htmx.HxConfirm("Are you sure you want to delete this webhook?"),
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package webhooks
|
||||
|
||||
import (
|
||||
lucide "github.com/eduardolat/gomponents-lucide"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/component"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/htmx"
|
||||
"github.com/google/uuid"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/maragudk/gomponents"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
lucide "github.com/nodxdev/nodxgo-lucide"
|
||||
)
|
||||
|
||||
func (h *handlers) duplicateWebhookHandler(c echo.Context) error {
|
||||
@@ -24,7 +24,7 @@ func (h *handlers) duplicateWebhookHandler(c echo.Context) error {
|
||||
return htmx.RespondRefresh(c)
|
||||
}
|
||||
|
||||
func duplicateWebhookButton(webhookID uuid.UUID) gomponents.Node {
|
||||
func duplicateWebhookButton(webhookID uuid.UUID) nodx.Node {
|
||||
return component.OptionsDropdownButton(
|
||||
htmx.HxPost("/dashboard/webhooks/"+webhookID.String()+"/duplicate"),
|
||||
htmx.HxConfirm("Are you sure you want to duplicate this webhook?"),
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"database/sql"
|
||||
"net/http"
|
||||
|
||||
lucide "github.com/eduardolat/gomponents-lucide"
|
||||
"github.com/eduardolat/pgbackweb/internal/database/dbgen"
|
||||
"github.com/eduardolat/pgbackweb/internal/util/echoutil"
|
||||
"github.com/eduardolat/pgbackweb/internal/validate"
|
||||
@@ -12,8 +11,8 @@ import (
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/htmx"
|
||||
"github.com/google/uuid"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
lucide "github.com/nodxdev/nodxgo-lucide"
|
||||
)
|
||||
|
||||
type editWebhookDTO struct {
|
||||
@@ -89,7 +88,7 @@ func (h *handlers) editWebhookFormHandler(c echo.Context) error {
|
||||
return htmx.RespondToastError(c, err.Error())
|
||||
}
|
||||
|
||||
return echoutil.RenderGomponent(c, http.StatusOK, editWebhookForm(
|
||||
return echoutil.RenderNodx(c, http.StatusOK, editWebhookForm(
|
||||
webhook, databases, destinations, backups,
|
||||
))
|
||||
}
|
||||
@@ -99,20 +98,20 @@ func editWebhookForm(
|
||||
databases []dbgen.DatabasesServiceGetAllDatabasesRow,
|
||||
destinations []dbgen.DestinationsServiceGetAllDestinationsRow,
|
||||
backups []dbgen.Backup,
|
||||
) gomponents.Node {
|
||||
return html.Form(
|
||||
) nodx.Node {
|
||||
return nodx.FormEl(
|
||||
htmx.HxPost("/dashboard/webhooks/"+webhook.ID.String()+"/edit"),
|
||||
htmx.HxDisabledELT("find button[type='submit']"),
|
||||
html.Class("space-y-2"),
|
||||
nodx.Class("space-y-2"),
|
||||
|
||||
createAndUpdateWebhookForm(databases, destinations, backups, webhook),
|
||||
|
||||
html.Div(
|
||||
html.Class("flex justify-end items-center space-x-2 pt-2"),
|
||||
nodx.Div(
|
||||
nodx.Class("flex justify-end items-center space-x-2 pt-2"),
|
||||
component.HxLoadingMd(),
|
||||
html.Button(
|
||||
html.Class("btn btn-primary"),
|
||||
html.Type("submit"),
|
||||
nodx.Button(
|
||||
nodx.Class("btn btn-primary"),
|
||||
nodx.Type("submit"),
|
||||
component.SpanText("Save"),
|
||||
lucide.Save(),
|
||||
),
|
||||
@@ -120,22 +119,22 @@ func editWebhookForm(
|
||||
)
|
||||
}
|
||||
|
||||
func editWebhookButton(webhookID uuid.UUID) gomponents.Node {
|
||||
func editWebhookButton(webhookID uuid.UUID) nodx.Node {
|
||||
mo := component.Modal(component.ModalParams{
|
||||
Size: component.SizeLg,
|
||||
Title: "Edit webhook",
|
||||
Content: []gomponents.Node{
|
||||
html.Div(
|
||||
Content: []nodx.Node{
|
||||
nodx.Div(
|
||||
htmx.HxGet("/dashboard/webhooks/"+webhookID.String()+"/edit"),
|
||||
htmx.HxSwap("outerHTML"),
|
||||
htmx.HxTrigger("intersect once"),
|
||||
html.Class("p-10 flex justify-center"),
|
||||
nodx.Class("p-10 flex justify-center"),
|
||||
component.HxLoadingMd(),
|
||||
),
|
||||
},
|
||||
})
|
||||
|
||||
return html.Div(
|
||||
return nodx.Div(
|
||||
mo.HTML,
|
||||
component.OptionsDropdownButton(
|
||||
mo.OpenerAttr,
|
||||
|
||||
@@ -9,42 +9,41 @@ import (
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/htmx"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/layout"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
)
|
||||
|
||||
func (h *handlers) indexPageHandler(c echo.Context) error {
|
||||
reqCtx := reqctx.GetCtx(c)
|
||||
return echoutil.RenderGomponent(
|
||||
return echoutil.RenderNodx(
|
||||
c, http.StatusOK, indexPage(reqCtx),
|
||||
)
|
||||
}
|
||||
|
||||
func indexPage(reqCtx reqctx.Ctx) gomponents.Node {
|
||||
content := []gomponents.Node{
|
||||
html.Div(
|
||||
html.Class("flex justify-between items-start"),
|
||||
func indexPage(reqCtx reqctx.Ctx) nodx.Node {
|
||||
content := []nodx.Node{
|
||||
nodx.Div(
|
||||
nodx.Class("flex justify-between items-start"),
|
||||
component.H1Text("Webhooks"),
|
||||
createWebhookButton(),
|
||||
),
|
||||
|
||||
component.CardBox(component.CardBoxParams{
|
||||
Class: "mt-4",
|
||||
Children: []gomponents.Node{
|
||||
html.Div(
|
||||
html.Class("overflow-x-auto"),
|
||||
html.Table(
|
||||
html.Class("table text-nowrap"),
|
||||
html.THead(
|
||||
html.Tr(
|
||||
html.Th(html.Class("w-1")),
|
||||
html.Th(component.SpanText("Name")),
|
||||
html.Th(component.SpanText("Event type")),
|
||||
html.Th(component.SpanText("Targets")),
|
||||
html.Th(component.SpanText("Created at")),
|
||||
Children: []nodx.Node{
|
||||
nodx.Div(
|
||||
nodx.Class("overflow-x-auto"),
|
||||
nodx.Table(
|
||||
nodx.Class("table text-nowrap"),
|
||||
nodx.Thead(
|
||||
nodx.Tr(
|
||||
nodx.Th(nodx.Class("w-1")),
|
||||
nodx.Th(component.SpanText("Name")),
|
||||
nodx.Th(component.SpanText("Event type")),
|
||||
nodx.Th(component.SpanText("Targets")),
|
||||
nodx.Th(component.SpanText("Created at")),
|
||||
),
|
||||
),
|
||||
html.TBody(
|
||||
nodx.Tbody(
|
||||
component.SkeletonTr(8),
|
||||
htmx.HxGet("/dashboard/webhooks/list?page=1"),
|
||||
htmx.HxTrigger("load"),
|
||||
|
||||
@@ -14,8 +14,7 @@ import (
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/component"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/htmx"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
)
|
||||
|
||||
func (h *handlers) listWebhooksHandler(c echo.Context) error {
|
||||
@@ -41,7 +40,7 @@ func (h *handlers) listWebhooksHandler(c echo.Context) error {
|
||||
return htmx.RespondToastError(c, err.Error())
|
||||
}
|
||||
|
||||
return echoutil.RenderGomponent(
|
||||
return echoutil.RenderNodx(
|
||||
c, http.StatusOK, listWebhooks(pagination, whooks),
|
||||
)
|
||||
}
|
||||
@@ -49,7 +48,7 @@ func (h *handlers) listWebhooksHandler(c echo.Context) error {
|
||||
func listWebhooks(
|
||||
pagination paginateutil.PaginateResponse,
|
||||
whooks []dbgen.Webhook,
|
||||
) gomponents.Node {
|
||||
) nodx.Node {
|
||||
if len(whooks) < 1 {
|
||||
return component.EmptyResultsTr(component.EmptyResultsParams{
|
||||
Title: "No webhooks found",
|
||||
@@ -57,24 +56,24 @@ func listWebhooks(
|
||||
})
|
||||
}
|
||||
|
||||
trs := []gomponents.Node{}
|
||||
trs := []nodx.Node{}
|
||||
for _, whook := range whooks {
|
||||
trs = append(trs, html.Tr(
|
||||
html.Td(component.OptionsDropdown(
|
||||
trs = append(trs, nodx.Tr(
|
||||
nodx.Td(component.OptionsDropdown(
|
||||
webhookExecutionsButton(whook.ID),
|
||||
runWebhookButton(whook.ID),
|
||||
editWebhookButton(whook.ID),
|
||||
duplicateWebhookButton(whook.ID),
|
||||
deleteWebhookButton(whook.ID),
|
||||
)),
|
||||
html.Td(
|
||||
html.Div(
|
||||
html.Class("flex items-center space-x-2"),
|
||||
nodx.Td(
|
||||
nodx.Div(
|
||||
nodx.Class("flex items-center space-x-2"),
|
||||
component.IsActivePing(whook.IsActive),
|
||||
component.SpanText(whook.Name),
|
||||
),
|
||||
),
|
||||
html.Td(component.SpanText(
|
||||
nodx.Td(component.SpanText(
|
||||
func() string {
|
||||
if name, ok := webhooks.FullEventTypes[whook.EventType]; ok {
|
||||
return name
|
||||
@@ -82,15 +81,15 @@ func listWebhooks(
|
||||
return whook.EventType
|
||||
}(),
|
||||
)),
|
||||
html.Td(component.SpanText(fmt.Sprintf("%d", len(whook.TargetIds)))),
|
||||
html.Td(component.SpanText(
|
||||
nodx.Td(component.SpanText(fmt.Sprintf("%d", len(whook.TargetIds)))),
|
||||
nodx.Td(component.SpanText(
|
||||
whook.CreatedAt.Local().Format(timeutil.LayoutYYYYMMDDHHMMSSPretty),
|
||||
)),
|
||||
))
|
||||
}
|
||||
|
||||
if pagination.HasNextPage {
|
||||
trs = append(trs, html.Tr(
|
||||
trs = append(trs, nodx.Tr(
|
||||
htmx.HxGet(func() string {
|
||||
url := "/dashboard/webhooks/list"
|
||||
url = strutil.AddQueryParamToUrl(url, "page", fmt.Sprintf("%d", pagination.NextPage))
|
||||
|
||||
@@ -3,13 +3,13 @@ package webhooks
|
||||
import (
|
||||
"context"
|
||||
|
||||
lucide "github.com/eduardolat/gomponents-lucide"
|
||||
"github.com/eduardolat/pgbackweb/internal/logger"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/component"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/htmx"
|
||||
"github.com/google/uuid"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/maragudk/gomponents"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
lucide "github.com/nodxdev/nodxgo-lucide"
|
||||
)
|
||||
|
||||
func (h *handlers) runWebhookHandler(c echo.Context) error {
|
||||
@@ -40,7 +40,7 @@ func (h *handlers) runWebhookHandler(c echo.Context) error {
|
||||
return htmx.RespondToastSuccess(c, "Running webhook, check the webhook executions for more details")
|
||||
}
|
||||
|
||||
func runWebhookButton(webhookID uuid.UUID) gomponents.Node {
|
||||
func runWebhookButton(webhookID uuid.UUID) nodx.Node {
|
||||
return component.OptionsDropdownButton(
|
||||
htmx.HxPost("/dashboard/webhooks/"+webhookID.String()+"/run"),
|
||||
htmx.HxDisabledELT("this"),
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
lucide "github.com/eduardolat/gomponents-lucide"
|
||||
"github.com/eduardolat/pgbackweb/internal/database/dbgen"
|
||||
"github.com/eduardolat/pgbackweb/internal/service/webhooks"
|
||||
"github.com/eduardolat/pgbackweb/internal/util/echoutil"
|
||||
@@ -18,8 +17,8 @@ import (
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/htmx"
|
||||
"github.com/google/uuid"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
lucide "github.com/nodxdev/nodxgo-lucide"
|
||||
)
|
||||
|
||||
func (h *handlers) paginateWebhookExecutionsHandler(c echo.Context) error {
|
||||
@@ -50,7 +49,7 @@ func (h *handlers) paginateWebhookExecutionsHandler(c echo.Context) error {
|
||||
return htmx.RespondToastError(c, err.Error())
|
||||
}
|
||||
|
||||
return echoutil.RenderGomponent(
|
||||
return echoutil.RenderNodx(
|
||||
c, http.StatusOK, webhookExecutionsList(webhookID, pagination, execs),
|
||||
)
|
||||
}
|
||||
@@ -59,7 +58,7 @@ func webhookExecutionsList(
|
||||
webhookID uuid.UUID,
|
||||
pagination paginateutil.PaginateResponse,
|
||||
execs []dbgen.WebhookExecution,
|
||||
) gomponents.Node {
|
||||
) nodx.Node {
|
||||
if len(execs) == 0 {
|
||||
return component.EmptyResultsTr(component.EmptyResultsParams{
|
||||
Title: "No executions found",
|
||||
@@ -67,26 +66,26 @@ func webhookExecutionsList(
|
||||
})
|
||||
}
|
||||
|
||||
trs := []gomponents.Node{}
|
||||
trs := []nodx.Node{}
|
||||
for _, exec := range execs {
|
||||
durationMillis := exec.ResDuration.Int32
|
||||
duration := time.Duration(durationMillis) * time.Millisecond
|
||||
|
||||
trs = append(trs, html.Tr(
|
||||
html.Td(
|
||||
trs = append(trs, nodx.Tr(
|
||||
nodx.Td(
|
||||
webhookExecutionDetailsButton(exec, duration),
|
||||
),
|
||||
html.Td(component.SpanText(fmt.Sprintf("%d", exec.ResStatus.Int16))),
|
||||
html.Td(component.SpanText(exec.ReqMethod.String)),
|
||||
html.Td(component.SpanText(duration.String())),
|
||||
html.Td(component.SpanText(
|
||||
nodx.Td(component.SpanText(fmt.Sprintf("%d", exec.ResStatus.Int16))),
|
||||
nodx.Td(component.SpanText(exec.ReqMethod.String)),
|
||||
nodx.Td(component.SpanText(duration.String())),
|
||||
nodx.Td(component.SpanText(
|
||||
exec.CreatedAt.Local().Format(timeutil.LayoutYYYYMMDDHHMMSSPretty),
|
||||
)),
|
||||
))
|
||||
}
|
||||
|
||||
if pagination.HasNextPage {
|
||||
trs = append(trs, html.Tr(
|
||||
trs = append(trs, nodx.Tr(
|
||||
htmx.HxGet(func() string {
|
||||
url := "/dashboard/webhooks/" + webhookID.String() + "/executions"
|
||||
url = strutil.AddQueryParamToUrl(url, "page", fmt.Sprintf("%d", pagination.NextPage))
|
||||
@@ -104,12 +103,12 @@ func webhookExecutionsList(
|
||||
func webhookExecutionDetailsButton(
|
||||
exec dbgen.WebhookExecution,
|
||||
duration time.Duration,
|
||||
) gomponents.Node {
|
||||
) nodx.Node {
|
||||
mo := component.Modal(component.ModalParams{
|
||||
Title: "Webhook execution details",
|
||||
Content: []gomponents.Node{
|
||||
html.Div(
|
||||
html.Class("space-y-4"),
|
||||
Content: []nodx.Node{
|
||||
nodx.Div(
|
||||
nodx.Class("space-y-4"),
|
||||
|
||||
alpine.XData(`{
|
||||
processTextareas() {
|
||||
@@ -127,98 +126,98 @@ func webhookExecutionDetailsButton(
|
||||
}`),
|
||||
alpine.XOn("mouseenter.once", "processTextareas()"),
|
||||
|
||||
html.Table(
|
||||
html.Class("table [&_th]:text-nowrap"),
|
||||
html.Tr(
|
||||
html.Td(
|
||||
html.ColSpan("100%"),
|
||||
nodx.Table(
|
||||
nodx.Class("table [&_th]:text-nowrap"),
|
||||
nodx.Tr(
|
||||
nodx.Td(
|
||||
nodx.Colspan("100%"),
|
||||
component.H3Text("General"),
|
||||
),
|
||||
),
|
||||
html.Tr(
|
||||
html.Th(component.SpanText("ID")),
|
||||
html.Td(component.SpanText(exec.ID.String())),
|
||||
nodx.Tr(
|
||||
nodx.Th(component.SpanText("ID")),
|
||||
nodx.Td(component.SpanText(exec.ID.String())),
|
||||
),
|
||||
html.Tr(
|
||||
html.Th(component.SpanText("Date")),
|
||||
html.Td(component.SpanText(
|
||||
nodx.Tr(
|
||||
nodx.Th(component.SpanText("Date")),
|
||||
nodx.Td(component.SpanText(
|
||||
exec.CreatedAt.Local().Format(timeutil.LayoutYYYYMMDDHHMMSSPretty),
|
||||
)),
|
||||
),
|
||||
),
|
||||
|
||||
html.Table(
|
||||
html.Class("table [&_th]:text-nowrap"),
|
||||
html.Tr(
|
||||
html.Td(
|
||||
html.ColSpan("100%"),
|
||||
nodx.Table(
|
||||
nodx.Class("table [&_th]:text-nowrap"),
|
||||
nodx.Tr(
|
||||
nodx.Td(
|
||||
nodx.Colspan("100%"),
|
||||
component.H3Text("Request"),
|
||||
),
|
||||
),
|
||||
html.Tr(
|
||||
html.Th(component.SpanText("Method")),
|
||||
html.Td(component.SpanText(exec.ReqMethod.String)),
|
||||
nodx.Tr(
|
||||
nodx.Th(component.SpanText("Method")),
|
||||
nodx.Td(component.SpanText(exec.ReqMethod.String)),
|
||||
),
|
||||
html.Tr(
|
||||
html.Th(component.SpanText("Headers")),
|
||||
html.Td(
|
||||
nodx.Tr(
|
||||
nodx.Th(component.SpanText("Headers")),
|
||||
nodx.Td(
|
||||
component.TextareaControl(component.TextareaControlParams{
|
||||
Children: []gomponents.Node{
|
||||
Children: []nodx.Node{
|
||||
alpine.XRef("reqHeadersTextarea"),
|
||||
gomponents.Text(exec.ReqHeaders.String),
|
||||
nodx.Text(exec.ReqHeaders.String),
|
||||
},
|
||||
}),
|
||||
),
|
||||
),
|
||||
html.Tr(
|
||||
html.Th(component.SpanText("Body")),
|
||||
html.Td(
|
||||
nodx.Tr(
|
||||
nodx.Th(component.SpanText("Body")),
|
||||
nodx.Td(
|
||||
component.TextareaControl(component.TextareaControlParams{
|
||||
Children: []gomponents.Node{
|
||||
Children: []nodx.Node{
|
||||
alpine.XRef("reqBodyTextarea"),
|
||||
gomponents.Text(exec.ReqBody.String),
|
||||
nodx.Text(exec.ReqBody.String),
|
||||
},
|
||||
}),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
html.Table(
|
||||
html.Class("table [&_th]:text-nowrap"),
|
||||
html.Tr(
|
||||
html.Td(
|
||||
html.ColSpan("100%"),
|
||||
nodx.Table(
|
||||
nodx.Class("table [&_th]:text-nowrap"),
|
||||
nodx.Tr(
|
||||
nodx.Td(
|
||||
nodx.Colspan("100%"),
|
||||
component.H3Text("Response"),
|
||||
),
|
||||
),
|
||||
html.Tr(
|
||||
html.Th(component.SpanText("Status")),
|
||||
html.Td(component.SpanText(
|
||||
nodx.Tr(
|
||||
nodx.Th(component.SpanText("Status")),
|
||||
nodx.Td(component.SpanText(
|
||||
fmt.Sprintf("%d", exec.ResStatus.Int16),
|
||||
)),
|
||||
),
|
||||
html.Tr(
|
||||
html.Th(component.SpanText("Duration")),
|
||||
html.Td(component.SpanText(duration.String())),
|
||||
nodx.Tr(
|
||||
nodx.Th(component.SpanText("Duration")),
|
||||
nodx.Td(component.SpanText(duration.String())),
|
||||
),
|
||||
html.Tr(
|
||||
html.Th(component.SpanText("Headers")),
|
||||
html.Td(
|
||||
nodx.Tr(
|
||||
nodx.Th(component.SpanText("Headers")),
|
||||
nodx.Td(
|
||||
component.TextareaControl(component.TextareaControlParams{
|
||||
Children: []gomponents.Node{
|
||||
Children: []nodx.Node{
|
||||
alpine.XRef("resHeadersTextarea"),
|
||||
gomponents.Text(exec.ResHeaders.String),
|
||||
nodx.Text(exec.ResHeaders.String),
|
||||
},
|
||||
}),
|
||||
),
|
||||
),
|
||||
html.Tr(
|
||||
html.Th(component.SpanText("Body")),
|
||||
html.Td(
|
||||
nodx.Tr(
|
||||
nodx.Th(component.SpanText("Body")),
|
||||
nodx.Td(
|
||||
component.TextareaControl(component.TextareaControlParams{
|
||||
Children: []gomponents.Node{
|
||||
Children: []nodx.Node{
|
||||
alpine.XRef("resBodyTextarea"),
|
||||
gomponents.Text(exec.ResBody.String),
|
||||
nodx.Text(exec.ResBody.String),
|
||||
},
|
||||
}),
|
||||
),
|
||||
@@ -228,35 +227,35 @@ func webhookExecutionDetailsButton(
|
||||
},
|
||||
})
|
||||
|
||||
return html.Div(
|
||||
html.Class("inline-block tooltip tooltip-right"),
|
||||
html.Data("tip", "More details"),
|
||||
return nodx.Div(
|
||||
nodx.Class("inline-block tooltip tooltip-right"),
|
||||
nodx.Data("tip", "More details"),
|
||||
mo.HTML,
|
||||
html.Button(
|
||||
html.Class("btn btn-error btn-square btn-sm btn-ghost"),
|
||||
nodx.Button(
|
||||
nodx.Class("btn btn-error btn-square btn-sm btn-ghost"),
|
||||
lucide.Eye(),
|
||||
mo.OpenerAttr,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
func webhookExecutionsButton(webhookID uuid.UUID) gomponents.Node {
|
||||
func webhookExecutionsButton(webhookID uuid.UUID) nodx.Node {
|
||||
mo := component.Modal(component.ModalParams{
|
||||
Size: component.SizeMd,
|
||||
Title: "Webhook executions",
|
||||
Content: []gomponents.Node{
|
||||
html.Table(
|
||||
html.Class("table"),
|
||||
html.THead(
|
||||
html.Tr(
|
||||
html.Th(html.Class("w-1")),
|
||||
html.Th(component.SpanText("Status")),
|
||||
html.Th(component.SpanText("Method")),
|
||||
html.Th(component.SpanText("Duration")),
|
||||
html.Th(component.SpanText("Date")),
|
||||
Content: []nodx.Node{
|
||||
nodx.Table(
|
||||
nodx.Class("table"),
|
||||
nodx.Thead(
|
||||
nodx.Tr(
|
||||
nodx.Th(nodx.Class("w-1")),
|
||||
nodx.Th(component.SpanText("Status")),
|
||||
nodx.Th(component.SpanText("Method")),
|
||||
nodx.Th(component.SpanText("Duration")),
|
||||
nodx.Th(component.SpanText("Date")),
|
||||
),
|
||||
),
|
||||
html.TBody(
|
||||
nodx.Tbody(
|
||||
htmx.HxGet(
|
||||
"/dashboard/webhooks/"+webhookID.String()+"/executions?page=1",
|
||||
),
|
||||
@@ -264,14 +263,14 @@ func webhookExecutionsButton(webhookID uuid.UUID) gomponents.Node {
|
||||
htmx.HxTrigger("intersect once"),
|
||||
),
|
||||
),
|
||||
html.Div(
|
||||
html.Class("flex justify-center pt-2"),
|
||||
nodx.Div(
|
||||
nodx.Class("flex justify-center pt-2"),
|
||||
component.HxLoadingMd("webhook-executions-loading"),
|
||||
),
|
||||
},
|
||||
})
|
||||
|
||||
return html.Div(
|
||||
return nodx.Div(
|
||||
mo.HTML,
|
||||
component.OptionsDropdownButton(
|
||||
mo.OpenerAttr,
|
||||
|
||||
@@ -1,269 +1,267 @@
|
||||
package htmx
|
||||
|
||||
import (
|
||||
"github.com/maragudk/gomponents"
|
||||
)
|
||||
import nodx "github.com/nodxdev/nodxgo"
|
||||
|
||||
// HxGet returns a gomponents node with the hx-get
|
||||
// HxGet returns a NodX node with the hx-get
|
||||
// attribute set to the given path.
|
||||
//
|
||||
// https://htmx.org/attributes/hx-get/
|
||||
func HxGet(path string) gomponents.Node {
|
||||
return gomponents.Attr("hx-get", path)
|
||||
func HxGet(path string) nodx.Node {
|
||||
return nodx.Attr("hx-get", path)
|
||||
}
|
||||
|
||||
// HxPost returns a gomponents node with the hx-post
|
||||
// HxPost returns a NodX node with the hx-post
|
||||
// attribute set to the given path.
|
||||
//
|
||||
// https://htmx.org/attributes/hx-post/
|
||||
func HxPost(path string) gomponents.Node {
|
||||
return gomponents.Attr("hx-post", path)
|
||||
func HxPost(path string) nodx.Node {
|
||||
return nodx.Attr("hx-post", path)
|
||||
}
|
||||
|
||||
// HxPut returns a gomponents node with the hx-put
|
||||
// HxPut returns a NodX node with the hx-put
|
||||
// attribute set to the given path.
|
||||
//
|
||||
// https://htmx.org/attributes/hx-put/
|
||||
func HxPut(path string) gomponents.Node {
|
||||
return gomponents.Attr("hx-put", path)
|
||||
func HxPut(path string) nodx.Node {
|
||||
return nodx.Attr("hx-put", path)
|
||||
}
|
||||
|
||||
// HxPatch returns a gomponents node with the hx-patch
|
||||
// HxPatch returns a NodX node with the hx-patch
|
||||
// attribute set to the given path.
|
||||
//
|
||||
// https://htmx.org/attributes/hx-patch/
|
||||
func HxPatch(path string) gomponents.Node {
|
||||
return gomponents.Attr("hx-patch", path)
|
||||
func HxPatch(path string) nodx.Node {
|
||||
return nodx.Attr("hx-patch", path)
|
||||
}
|
||||
|
||||
// HxDelete returns a gomponents node with the hx-delete
|
||||
// HxDelete returns a NodX node with the hx-delete
|
||||
// attribute set to the given path.
|
||||
//
|
||||
// https://htmx.org/attributes/hx-delete/
|
||||
func HxDelete(path string) gomponents.Node {
|
||||
return gomponents.Attr("hx-delete", path)
|
||||
func HxDelete(path string) nodx.Node {
|
||||
return nodx.Attr("hx-delete", path)
|
||||
}
|
||||
|
||||
// HxTrigger returns a gomponents node with the hx-trigger
|
||||
// HxTrigger returns a NodX node with the hx-trigger
|
||||
// attribute set to the given value.
|
||||
//
|
||||
// https://htmx.org/attributes/hx-trigger/
|
||||
func HxTrigger(value string) gomponents.Node {
|
||||
return gomponents.Attr("hx-trigger", value)
|
||||
func HxTrigger(value string) nodx.Node {
|
||||
return nodx.Attr("hx-trigger", value)
|
||||
}
|
||||
|
||||
// HxTarget returns a gomponents node with the hx-target
|
||||
// HxTarget returns a NodX node with the hx-target
|
||||
// attribute set to the given value.
|
||||
//
|
||||
// https://htmx.org/attributes/hx-target/
|
||||
func HxTarget(value string) gomponents.Node {
|
||||
return gomponents.Attr("hx-target", value)
|
||||
func HxTarget(value string) nodx.Node {
|
||||
return nodx.Attr("hx-target", value)
|
||||
}
|
||||
|
||||
// HxSwap returns a gomponents node with the hx-swap
|
||||
// HxSwap returns a NodX node with the hx-swap
|
||||
// attribute set to the given value.
|
||||
//
|
||||
// https://htmx.org/attributes/hx-swap/
|
||||
func HxSwap(value string) gomponents.Node {
|
||||
return gomponents.Attr("hx-swap", value)
|
||||
func HxSwap(value string) nodx.Node {
|
||||
return nodx.Attr("hx-swap", value)
|
||||
}
|
||||
|
||||
// HxIndicator returns a gomponents node with the hx-indicator
|
||||
// HxIndicator returns a NodX node with the hx-indicator
|
||||
// attribute set to the given value.
|
||||
//
|
||||
// https://htmx.org/attributes/hx-indicator/
|
||||
func HxIndicator(value string) gomponents.Node {
|
||||
return gomponents.Attr("hx-indicator", value)
|
||||
func HxIndicator(value string) nodx.Node {
|
||||
return nodx.Attr("hx-indicator", value)
|
||||
}
|
||||
|
||||
// HxConfirm returns a gomponents node with the hx-confirm
|
||||
// HxConfirm returns a NodX node with the hx-confirm
|
||||
// attribute set to the given value.
|
||||
//
|
||||
// https://htmx.org/attributes/hx-confirm/
|
||||
func HxConfirm(value string) gomponents.Node {
|
||||
return gomponents.Attr("hx-confirm", value)
|
||||
func HxConfirm(value string) nodx.Node {
|
||||
return nodx.Attr("hx-confirm", value)
|
||||
}
|
||||
|
||||
// HxBoost returns a gomponents node with the hx-boost
|
||||
// HxBoost returns a NodX node with the hx-boost
|
||||
// attribute set to the given value.
|
||||
//
|
||||
// See https://htmx.org/attributes/hx-boost/
|
||||
func HxBoost(value string) gomponents.Node {
|
||||
return gomponents.Attr("hx-boost", value)
|
||||
func HxBoost(value string) nodx.Node {
|
||||
return nodx.Attr("hx-boost", value)
|
||||
}
|
||||
|
||||
// HxOn returns a gomponents node with the hx-on:name="value"
|
||||
// HxOn returns a NodX node with the hx-on:name="value"
|
||||
// attribute set to the given value.
|
||||
//
|
||||
// https://htmx.org/attributes/hx-on/
|
||||
func HxOn(name string, value string) gomponents.Node {
|
||||
return gomponents.Attr("hx-on:"+name, value)
|
||||
func HxOn(name string, value string) nodx.Node {
|
||||
return nodx.Attr("hx-on:"+name, value)
|
||||
}
|
||||
|
||||
// HxPushURL returns a gomponents node with the hx-push-url
|
||||
// HxPushURL returns a NodX node with the hx-push-url
|
||||
// attribute set to the given value.
|
||||
//
|
||||
// https://htmx.org/attributes/hx-push-url/
|
||||
func HxPushURL(value string) gomponents.Node {
|
||||
return gomponents.Attr("hx-push-url", value)
|
||||
func HxPushURL(value string) nodx.Node {
|
||||
return nodx.Attr("hx-push-url", value)
|
||||
}
|
||||
|
||||
// HxSelect returns a gomponents node with the hx-select
|
||||
// HxSelect returns a NodX node with the hx-select
|
||||
// attribute set to the given value.
|
||||
//
|
||||
// https://htmx.org/attributes/hx-select/
|
||||
func HxSelect(value string) gomponents.Node {
|
||||
return gomponents.Attr("hx-select", value)
|
||||
func HxSelect(value string) nodx.Node {
|
||||
return nodx.Attr("hx-select", value)
|
||||
}
|
||||
|
||||
// HxSelectOOB returns a gomponents node with the hx-select-oob
|
||||
// HxSelectOOB returns a NodX node with the hx-select-oob
|
||||
// attribute set to the given value.
|
||||
//
|
||||
// https://htmx.org/attributes/hx-select-oob/
|
||||
func HxSelectOOB(value string) gomponents.Node {
|
||||
return gomponents.Attr("hx-select-oob", value)
|
||||
func HxSelectOOB(value string) nodx.Node {
|
||||
return nodx.Attr("hx-select-oob", value)
|
||||
}
|
||||
|
||||
// HxSwapOOB returns a gomponents node with the hx-swap-oob
|
||||
// HxSwapOOB returns a NodX node with the hx-swap-oob
|
||||
// attribute set to the given value.
|
||||
//
|
||||
// https://htmx.org/attributes/hx-swap-oob/
|
||||
func HxSwapOOB(value string) gomponents.Node {
|
||||
return gomponents.Attr("hx-swap-oob", value)
|
||||
func HxSwapOOB(value string) nodx.Node {
|
||||
return nodx.Attr("hx-swap-oob", value)
|
||||
}
|
||||
|
||||
// HxVals returns a gomponents node with the hx-vals
|
||||
// HxVals returns a NodX node with the hx-vals
|
||||
// attribute set to the given value.
|
||||
//
|
||||
// https://htmx.org/attributes/hx-vals/
|
||||
func HxVals(value string) gomponents.Node {
|
||||
return gomponents.Attr("hx-vals", value)
|
||||
func HxVals(value string) nodx.Node {
|
||||
return nodx.Attr("hx-vals", value)
|
||||
}
|
||||
|
||||
// HxDisable returns a gomponents node with the hx-disable
|
||||
// HxDisable returns a NodX node with the hx-disable
|
||||
// attribute set to the given value.
|
||||
//
|
||||
// https://htmx.org/attributes/hx-disable/
|
||||
func HxDisable(value string) gomponents.Node {
|
||||
return gomponents.Attr("hx-disable", value)
|
||||
func HxDisable(value string) nodx.Node {
|
||||
return nodx.Attr("hx-disable", value)
|
||||
}
|
||||
|
||||
// HxDisabledELT returns a gomponents node with the hx-disabled-elt
|
||||
// HxDisabledELT returns a NodX node with the hx-disabled-elt
|
||||
// attribute set to the given value.
|
||||
//
|
||||
// https://htmx.org/attributes/hx-disabled-elt/
|
||||
func HxDisabledELT(value string) gomponents.Node {
|
||||
return gomponents.Attr("hx-disabled-elt", value)
|
||||
func HxDisabledELT(value string) nodx.Node {
|
||||
return nodx.Attr("hx-disabled-elt", value)
|
||||
}
|
||||
|
||||
// HxDisinherit returns a gomponents node with the hx-disinherit
|
||||
// HxDisinherit returns a NodX node with the hx-disinherit
|
||||
// attribute set to the given value.
|
||||
//
|
||||
// https://htmx.org/attributes/hx-disinherit/
|
||||
func HxDisinherit(value string) gomponents.Node {
|
||||
return gomponents.Attr("hx-disinherit", value)
|
||||
func HxDisinherit(value string) nodx.Node {
|
||||
return nodx.Attr("hx-disinherit", value)
|
||||
}
|
||||
|
||||
// HxEncoding returns a gomponents node with the hx-encoding
|
||||
// HxEncoding returns a NodX node with the hx-encoding
|
||||
// attribute set to the given value.
|
||||
//
|
||||
// https://htmx.org/attributes/hx-encoding/
|
||||
func HxEncoding(value string) gomponents.Node {
|
||||
return gomponents.Attr("hx-encoding", value)
|
||||
func HxEncoding(value string) nodx.Node {
|
||||
return nodx.Attr("hx-encoding", value)
|
||||
}
|
||||
|
||||
// HxExt returns a gomponents node with the hx-ext
|
||||
// HxExt returns a NodX node with the hx-ext
|
||||
// attribute set to the given value.
|
||||
//
|
||||
// https://htmx.org/attributes/hx-ext/
|
||||
func HxExt(value string) gomponents.Node {
|
||||
return gomponents.Attr("hx-ext", value)
|
||||
func HxExt(value string) nodx.Node {
|
||||
return nodx.Attr("hx-ext", value)
|
||||
}
|
||||
|
||||
// HxHeaders returns a gomponents node with the hx-headers
|
||||
// HxHeaders returns a NodX node with the hx-headers
|
||||
// attribute set to the given value.
|
||||
//
|
||||
// https://htmx.org/attributes/hx-headers/
|
||||
func HxHeaders(value string) gomponents.Node {
|
||||
return gomponents.Attr("hx-headers", value)
|
||||
func HxHeaders(value string) nodx.Node {
|
||||
return nodx.Attr("hx-headers", value)
|
||||
}
|
||||
|
||||
// HxHistory returns a gomponents node with the hx-history
|
||||
// HxHistory returns a NodX node with the hx-history
|
||||
// attribute set to the given value.
|
||||
//
|
||||
// https://htmx.org/attributes/hx-history/
|
||||
func HxHistory(value string) gomponents.Node {
|
||||
return gomponents.Attr("hx-history", value)
|
||||
func HxHistory(value string) nodx.Node {
|
||||
return nodx.Attr("hx-history", value)
|
||||
}
|
||||
|
||||
// HxHistoryElt returns a gomponents node with the hx-history-elt
|
||||
// HxHistoryElt returns a NodX node with the hx-history-elt
|
||||
// attribute set to the given value.
|
||||
//
|
||||
// https://htmx.org/attributes/hx-history-elt/
|
||||
func HxHistoryElt(value string) gomponents.Node {
|
||||
return gomponents.Attr("hx-history-elt", value)
|
||||
func HxHistoryElt(value string) nodx.Node {
|
||||
return nodx.Attr("hx-history-elt", value)
|
||||
}
|
||||
|
||||
// HxInclude returns a gomponents node with the hx-include
|
||||
// HxInclude returns a NodX node with the hx-include
|
||||
// attribute set to the given value.
|
||||
//
|
||||
// https://htmx.org/attributes/hx-include/
|
||||
func HxInclude(value string) gomponents.Node {
|
||||
return gomponents.Attr("hx-include", value)
|
||||
func HxInclude(value string) nodx.Node {
|
||||
return nodx.Attr("hx-include", value)
|
||||
}
|
||||
|
||||
// HxParams returns a gomponents node with the hx-params
|
||||
// HxParams returns a NodX node with the hx-params
|
||||
// attribute set to the given value.
|
||||
//
|
||||
// https://htmx.org/attributes/hx-params/
|
||||
func HxParams(value string) gomponents.Node {
|
||||
return gomponents.Attr("hx-params", value)
|
||||
func HxParams(value string) nodx.Node {
|
||||
return nodx.Attr("hx-params", value)
|
||||
}
|
||||
|
||||
// HxPreserve returns a gomponents node with the hx-preserve
|
||||
// HxPreserve returns a NodX node with the hx-preserve
|
||||
// attribute set to the given value.
|
||||
//
|
||||
// https://htmx.org/attributes/hx-preserve/
|
||||
func HxPreserve(value string) gomponents.Node {
|
||||
return gomponents.Attr("hx-preserve", value)
|
||||
func HxPreserve(value string) nodx.Node {
|
||||
return nodx.Attr("hx-preserve", value)
|
||||
}
|
||||
|
||||
// HxPrompt returns a gomponents node with the hx-prompt
|
||||
// HxPrompt returns a NodX node with the hx-prompt
|
||||
// attribute set to the given value.
|
||||
//
|
||||
// https://htmx.org/attributes/hx-prompt/
|
||||
func HxPrompt(value string) gomponents.Node {
|
||||
return gomponents.Attr("hx-prompt", value)
|
||||
func HxPrompt(value string) nodx.Node {
|
||||
return nodx.Attr("hx-prompt", value)
|
||||
}
|
||||
|
||||
// HxReplaceURL returns a gomponents node with the hx-replace-url
|
||||
// HxReplaceURL returns a NodX node with the hx-replace-url
|
||||
// attribute set to the given value.
|
||||
//
|
||||
// https://htmx.org/attributes/hx-replace-url/
|
||||
func HxReplaceURL(value string) gomponents.Node {
|
||||
return gomponents.Attr("hx-replace-url", value)
|
||||
func HxReplaceURL(value string) nodx.Node {
|
||||
return nodx.Attr("hx-replace-url", value)
|
||||
}
|
||||
|
||||
// HxRequest returns a gomponents node with the hx-request
|
||||
// HxRequest returns a NodX node with the hx-request
|
||||
// attribute set to the given value.
|
||||
//
|
||||
// https://htmx.org/attributes/hx-request/
|
||||
func HxRequest(value string) gomponents.Node {
|
||||
return gomponents.Attr("hx-request", value)
|
||||
func HxRequest(value string) nodx.Node {
|
||||
return nodx.Attr("hx-request", value)
|
||||
}
|
||||
|
||||
// HxSync returns a gomponents node with the hx-sync
|
||||
// HxSync returns a NodX node with the hx-sync
|
||||
// attribute set to the given value.
|
||||
//
|
||||
// https://htmx.org/attributes/hx-sync/
|
||||
func HxSync(value string) gomponents.Node {
|
||||
return gomponents.Attr("hx-sync", value)
|
||||
func HxSync(value string) nodx.Node {
|
||||
return nodx.Attr("hx-sync", value)
|
||||
}
|
||||
|
||||
// HxValidate returns a gomponents node with the hx-validate
|
||||
// HxValidate returns a NodX node with the hx-validate
|
||||
// attribute set to the given value.
|
||||
//
|
||||
// https://htmx.org/attributes/hx-validate/
|
||||
func HxValidate(value string) gomponents.Node {
|
||||
return gomponents.Attr("hx-validate", value)
|
||||
func HxValidate(value string) nodx.Node {
|
||||
return nodx.Attr("hx-validate", value)
|
||||
}
|
||||
|
||||
@@ -2,54 +2,47 @@ package layout
|
||||
|
||||
import (
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/component"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/components"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
)
|
||||
|
||||
type AuthParams struct {
|
||||
Title string
|
||||
Body []gomponents.Node
|
||||
Body []nodx.Node
|
||||
}
|
||||
|
||||
func Auth(params AuthParams) gomponents.Node {
|
||||
func Auth(params AuthParams) nodx.Node {
|
||||
title := "PG Back Web"
|
||||
if params.Title != "" {
|
||||
title = params.Title + " - " + title
|
||||
}
|
||||
|
||||
return components.HTML5(components.HTML5Props{
|
||||
Language: "en",
|
||||
Title: title,
|
||||
Head: []gomponents.Node{
|
||||
head(),
|
||||
body := nodx.Group(
|
||||
nodx.ClassMap{
|
||||
"w-screen h-screen px-4 py-[40px]": true,
|
||||
"grid grid-cols-1 place-items-center": true,
|
||||
"bg-base-300 overflow-y-auto": true,
|
||||
},
|
||||
Body: []gomponents.Node{
|
||||
components.Classes{
|
||||
"w-screen h-screen px-4 py-[40px]": true,
|
||||
"grid grid-cols-1 place-items-center": true,
|
||||
"bg-base-300 overflow-y-auto": true,
|
||||
},
|
||||
html.Div(
|
||||
html.Class("w-full max-w-[600px] space-y-4"),
|
||||
html.Div(
|
||||
html.Class("flex justify-center"),
|
||||
component.Logotype(),
|
||||
),
|
||||
html.Main(
|
||||
html.Class("rounded-box shadow-md bg-base-100 p-4"),
|
||||
gomponents.Group(params.Body),
|
||||
),
|
||||
html.Div(
|
||||
html.Class("flex justify-start space-x-2 items-center"),
|
||||
component.ChangeThemeButton(component.ChangeThemeButtonParams{
|
||||
Position: component.DropdownPositionTop,
|
||||
AlignsToEnd: false,
|
||||
Size: component.SizeMd,
|
||||
}),
|
||||
component.StarOnGithub(component.SizeMd),
|
||||
),
|
||||
nodx.Div(
|
||||
nodx.Class("w-full max-w-[600px] space-y-4"),
|
||||
nodx.Div(
|
||||
nodx.Class("flex justify-center"),
|
||||
component.Logotype(),
|
||||
),
|
||||
},
|
||||
})
|
||||
nodx.Main(
|
||||
nodx.Class("rounded-box shadow-md bg-base-100 p-4"),
|
||||
nodx.Group(params.Body...),
|
||||
),
|
||||
nodx.Div(
|
||||
nodx.Class("flex justify-start space-x-2 items-center"),
|
||||
component.ChangeThemeButton(component.ChangeThemeButtonParams{
|
||||
Position: component.DropdownPositionTop,
|
||||
AlignsToEnd: false,
|
||||
Size: component.SizeMd,
|
||||
}),
|
||||
component.StarOnGithub(component.SizeMd),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
return commonHtmlDoc(title, body)
|
||||
}
|
||||
|
||||
@@ -2,33 +2,49 @@ package layout
|
||||
|
||||
import (
|
||||
"github.com/eduardolat/pgbackweb/internal/view/static"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
)
|
||||
|
||||
func head() gomponents.Node {
|
||||
href := func(path string) gomponents.Node {
|
||||
return html.Href(static.GetVersionedFilePath(path))
|
||||
}
|
||||
|
||||
src := func(path string) gomponents.Node {
|
||||
return html.Src(static.GetVersionedFilePath(path))
|
||||
}
|
||||
|
||||
return gomponents.Group([]gomponents.Node{
|
||||
html.Link(html.Rel("shortcut icon"), href("/favicon.ico")),
|
||||
html.Link(html.Rel("stylesheet"), href("/build/style.min.css")),
|
||||
html.Script(src("/build/app.min.js")),
|
||||
|
||||
html.Script(src("/libs/htmx/htmx-2.0.1.min.js"), html.Defer()),
|
||||
html.Script(src("/libs/alpinejs/alpinejs-3.14.1.min.js"), html.Defer()),
|
||||
html.Script(src("/libs/sweetalert2/sweetalert2-11.13.1.min.js")),
|
||||
html.Script(src("/libs/chartjs/chartjs-4.4.3.umd.min.js")),
|
||||
|
||||
html.Link(html.Rel("stylesheet"), href("/libs/notyf/notyf-3.10.0.min.css")),
|
||||
html.Script(src("/libs/notyf/notyf-3.10.0.min.js")),
|
||||
|
||||
html.Link(html.Rel("stylesheet"), href("/libs/slim-select/slimselect-2.8.2.css")),
|
||||
html.Script(src("/libs/slim-select/slimselect-2.8.2.min.js")),
|
||||
})
|
||||
func commonHtmlDoc(title string, bodyContentGroup nodx.Node) nodx.Node {
|
||||
return nodx.Group(
|
||||
nodx.DocType(),
|
||||
nodx.Html(
|
||||
nodx.Lang("en"),
|
||||
nodx.Head(
|
||||
nodx.TitleEl(nodx.Text(title)),
|
||||
commonHead(),
|
||||
),
|
||||
nodx.Body(bodyContentGroup),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
func commonHead() nodx.Node {
|
||||
href := func(path string) nodx.Node {
|
||||
return nodx.Href(static.GetVersionedFilePath(path))
|
||||
}
|
||||
|
||||
src := func(path string) nodx.Node {
|
||||
return nodx.Src(static.GetVersionedFilePath(path))
|
||||
}
|
||||
|
||||
return nodx.Group(
|
||||
nodx.Meta(nodx.Charset("utf-8")),
|
||||
nodx.Meta(nodx.Name("viewport"), nodx.Content("width=device-width, initial-scale=1")),
|
||||
|
||||
nodx.Link(nodx.Rel("shortcut icon"), href("/favicon.ico")),
|
||||
nodx.Link(nodx.Rel("stylesheet"), href("/build/style.min.css")),
|
||||
nodx.Script(src("/build/app.min.js")),
|
||||
|
||||
nodx.Script(src("/libs/htmx/htmx-2.0.1.min.js"), nodx.Defer("")),
|
||||
nodx.Script(src("/libs/alpinejs/alpinejs-3.14.1.min.js"), nodx.Defer("")),
|
||||
nodx.Script(src("/libs/sweetalert2/sweetalert2-11.13.1.min.js")),
|
||||
nodx.Script(src("/libs/chartjs/chartjs-4.4.3.umd.min.js")),
|
||||
|
||||
nodx.Link(nodx.Rel("stylesheet"), href("/libs/notyf/notyf-3.10.0.min.css")),
|
||||
nodx.Script(src("/libs/notyf/notyf-3.10.0.min.js")),
|
||||
|
||||
nodx.Link(nodx.Rel("stylesheet"), href("/libs/slim-select/slimselect-2.8.2.css")),
|
||||
nodx.Script(src("/libs/slim-select/slimselect-2.8.2.min.js")),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4,49 +4,42 @@ import (
|
||||
"github.com/eduardolat/pgbackweb/internal/view/reqctx"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/component"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/htmx"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/components"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
)
|
||||
|
||||
type DashboardParams struct {
|
||||
Title string
|
||||
Body []gomponents.Node
|
||||
Body []nodx.Node
|
||||
}
|
||||
|
||||
func Dashboard(reqCtx reqctx.Ctx, params DashboardParams) gomponents.Node {
|
||||
func Dashboard(reqCtx reqctx.Ctx, params DashboardParams) nodx.Node {
|
||||
title := "PG Back Web"
|
||||
if params.Title != "" {
|
||||
title = params.Title + " - " + title
|
||||
}
|
||||
|
||||
if reqCtx.IsHTMXBoosted {
|
||||
body := append(params.Body, html.TitleEl(gomponents.Text(title)))
|
||||
body := append(params.Body, nodx.TitleEl(nodx.Text(title)))
|
||||
return component.RenderableGroup(body)
|
||||
}
|
||||
|
||||
return components.HTML5(components.HTML5Props{
|
||||
Language: "en",
|
||||
Title: title,
|
||||
Head: []gomponents.Node{
|
||||
head(),
|
||||
body := nodx.Group(
|
||||
htmx.HxIndicator("#header-indicator"),
|
||||
nodx.ClassMap{
|
||||
"w-screen h-screen bg-base-200": true,
|
||||
"flex justify-start overflow-hidden": true,
|
||||
},
|
||||
Body: []gomponents.Node{
|
||||
htmx.HxIndicator("#header-indicator"),
|
||||
components.Classes{
|
||||
"w-screen h-screen bg-base-200": true,
|
||||
"flex justify-start overflow-hidden": true,
|
||||
},
|
||||
dashboardAside(),
|
||||
html.Div(
|
||||
html.Class("flex-grow overflow-y-auto"),
|
||||
dashboardHeader(),
|
||||
html.Main(
|
||||
html.ID("dashboard-main"),
|
||||
html.Class("p-4"),
|
||||
gomponents.Group(params.Body),
|
||||
),
|
||||
dashboardAside(),
|
||||
nodx.Div(
|
||||
nodx.Class("flex-grow overflow-y-auto"),
|
||||
dashboardHeader(),
|
||||
nodx.Main(
|
||||
nodx.Id("dashboard-main"),
|
||||
nodx.Class("p-4"),
|
||||
nodx.Group(params.Body...),
|
||||
),
|
||||
},
|
||||
})
|
||||
),
|
||||
)
|
||||
|
||||
return commonHtmlDoc(title, body)
|
||||
}
|
||||
|
||||
@@ -3,49 +3,47 @@ package layout
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
lucide "github.com/eduardolat/gomponents-lucide"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/alpine"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/htmx"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/components"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
lucide "github.com/nodxdev/nodxgo-lucide"
|
||||
)
|
||||
|
||||
func dashboardAside() gomponents.Node {
|
||||
return html.Aside(
|
||||
html.ID("dashboard-aside"),
|
||||
components.Classes{
|
||||
func dashboardAside() nodx.Node {
|
||||
return nodx.Aside(
|
||||
nodx.Id("dashboard-aside"),
|
||||
nodx.ClassMap{
|
||||
"flex-none h-[100dvh] bg-base-300 shadow-sm p-4": true,
|
||||
"overflow-y-auto overflow-x-hidden": true,
|
||||
},
|
||||
|
||||
html.A(
|
||||
html.Class("block flex flex-col justify-center items-center"),
|
||||
html.Href("https://github.com/eduardolat/pgbackweb"),
|
||||
html.Target("_blank"),
|
||||
html.Img(
|
||||
html.Src("/images/logo.png"),
|
||||
html.Alt("PG Back Web"),
|
||||
html.Class("w-[50px] h-auto"),
|
||||
nodx.A(
|
||||
nodx.Class("block flex flex-col justify-center items-center"),
|
||||
nodx.Href("https://github.com/eduardolat/pgbackweb"),
|
||||
nodx.Target("_blank"),
|
||||
nodx.Img(
|
||||
nodx.Src("/images/logo.png"),
|
||||
nodx.Alt("PG Back Web"),
|
||||
nodx.Class("w-[50px] h-auto"),
|
||||
),
|
||||
html.Span(
|
||||
html.Class("text-xs text-nowrap text-center font-bold mt-1"),
|
||||
html.Span(
|
||||
html.Class("block"),
|
||||
gomponents.Text("PG Back"),
|
||||
nodx.SpanEl(
|
||||
nodx.Class("text-xs text-nowrap text-center font-bold mt-1"),
|
||||
nodx.SpanEl(
|
||||
nodx.Class("block"),
|
||||
nodx.Text("PG Back"),
|
||||
),
|
||||
html.Span(
|
||||
html.Class("block"),
|
||||
gomponents.Text("Web"),
|
||||
nodx.SpanEl(
|
||||
nodx.Class("block"),
|
||||
nodx.Text("Web"),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
html.Div(
|
||||
nodx.Div(
|
||||
htmx.HxBoost("true"),
|
||||
htmx.HxTarget("#dashboard-main"),
|
||||
htmx.HxSwap("transition:true show:unset"),
|
||||
html.Class("mt-6 space-y-4"),
|
||||
nodx.Class("mt-6 space-y-4"),
|
||||
|
||||
dashboardAsideItem(
|
||||
lucide.LayoutDashboard,
|
||||
@@ -114,21 +112,21 @@ func dashboardAside() gomponents.Node {
|
||||
}
|
||||
|
||||
func dashboardAsideItem(
|
||||
icon func(children ...gomponents.Node) gomponents.Node,
|
||||
icon func(children ...nodx.Node) nodx.Node,
|
||||
text, link string, strict bool,
|
||||
) gomponents.Node {
|
||||
return html.A(
|
||||
) nodx.Node {
|
||||
return nodx.A(
|
||||
alpine.XData(fmt.Sprintf("alpineDashboardAsideItem('%s', %t)", link, strict)),
|
||||
html.Class("block flex flex-col items-center justify-center group"),
|
||||
html.Href(link),
|
||||
html.Button(
|
||||
nodx.Class("block flex flex-col items-center justify-center group"),
|
||||
nodx.Href(link),
|
||||
nodx.Button(
|
||||
alpine.XBind("class", `{'btn-active': is_active}`),
|
||||
html.Class("btn btn-ghost btn-neutral btn-square group-hover:btn-active"),
|
||||
icon(html.Class("size-6")),
|
||||
nodx.Class("btn btn-ghost btn-neutral btn-square group-hover:btn-active"),
|
||||
icon(nodx.Class("size-6")),
|
||||
),
|
||||
html.Span(
|
||||
html.Class("text-xs"),
|
||||
gomponents.Text(text),
|
||||
nodx.SpanEl(
|
||||
nodx.Class("text-xs"),
|
||||
nodx.Text(text),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,23 +1,21 @@
|
||||
package layout
|
||||
|
||||
import (
|
||||
lucide "github.com/eduardolat/gomponents-lucide"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/component"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/htmx"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/components"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
lucide "github.com/nodxdev/nodxgo-lucide"
|
||||
)
|
||||
|
||||
func dashboardHeader() gomponents.Node {
|
||||
return html.Header(
|
||||
components.Classes{
|
||||
func dashboardHeader() nodx.Node {
|
||||
return nodx.Header(
|
||||
nodx.ClassMap{
|
||||
"w-[full] bg-base-200 p-4 shadow-sm": true,
|
||||
"flex items-center justify-between": true,
|
||||
"sticky top-0 z-50": true,
|
||||
},
|
||||
html.Div(
|
||||
html.Class("flex justify-start items-center space-x-2"),
|
||||
nodx.Div(
|
||||
nodx.Class("flex justify-start items-center space-x-2"),
|
||||
component.ChangeThemeButton(component.ChangeThemeButtonParams{
|
||||
Position: component.DropdownPositionBottom,
|
||||
AlignsToEnd: true,
|
||||
@@ -27,24 +25,24 @@ func dashboardHeader() gomponents.Node {
|
||||
dashboardHeaderUpdates(),
|
||||
component.HxLoadingMd("header-indicator"),
|
||||
),
|
||||
html.Div(
|
||||
html.Class("flex justify-end items-center space-x-2"),
|
||||
html.Div(
|
||||
nodx.Div(
|
||||
nodx.Class("flex justify-end items-center space-x-2"),
|
||||
nodx.Div(
|
||||
htmx.HxGet("/dashboard/health-button"),
|
||||
htmx.HxSwap("outerHTML"),
|
||||
htmx.HxTrigger("load once"),
|
||||
),
|
||||
html.A(
|
||||
html.Href("https://discord.gg/BmAwq29UZ8"),
|
||||
html.Target("_blank"),
|
||||
html.Class("btn btn-ghost btn-neutral"),
|
||||
nodx.A(
|
||||
nodx.Href("https://discord.gg/BmAwq29UZ8"),
|
||||
nodx.Target("_blank"),
|
||||
nodx.Class("btn btn-ghost btn-neutral"),
|
||||
component.SpanText("Chat on Discord"),
|
||||
lucide.ExternalLink(),
|
||||
),
|
||||
html.Button(
|
||||
nodx.Button(
|
||||
htmx.HxPost("/auth/logout"),
|
||||
htmx.HxDisabledELT("this"),
|
||||
html.Class("btn btn-ghost btn-neutral"),
|
||||
nodx.Class("btn btn-ghost btn-neutral"),
|
||||
component.SpanText("Log out"),
|
||||
lucide.LogOut(),
|
||||
),
|
||||
|
||||
@@ -3,16 +3,15 @@ package layout
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
lucide "github.com/eduardolat/gomponents-lucide"
|
||||
"github.com/eduardolat/pgbackweb/internal/config"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/alpine"
|
||||
"github.com/eduardolat/pgbackweb/internal/view/web/component"
|
||||
"github.com/maragudk/gomponents"
|
||||
"github.com/maragudk/gomponents/html"
|
||||
nodx "github.com/nodxdev/nodxgo"
|
||||
lucide "github.com/nodxdev/nodxgo-lucide"
|
||||
)
|
||||
|
||||
func dashboardHeaderUpdates() gomponents.Node {
|
||||
return html.A(
|
||||
func dashboardHeaderUpdates() nodx.Node {
|
||||
return nodx.A(
|
||||
alpine.XData("alpineDashboardHeaderUpdates()"),
|
||||
alpine.XCloak(),
|
||||
alpine.XShow(fmt.Sprintf(
|
||||
@@ -20,12 +19,12 @@ func dashboardHeaderUpdates() gomponents.Node {
|
||||
config.Version,
|
||||
)),
|
||||
|
||||
html.Class("btn btn-warning"),
|
||||
html.Href("https://github.com/eduardolat/pgbackweb/releases"),
|
||||
html.Target("_blank"),
|
||||
nodx.Class("btn btn-warning"),
|
||||
nodx.Href("https://github.com/eduardolat/pgbackweb/releases"),
|
||||
nodx.Target("_blank"),
|
||||
lucide.ExternalLink(),
|
||||
component.SpanText("Update available"),
|
||||
html.Span(
|
||||
nodx.SpanEl(
|
||||
alpine.XText("'( ' + latestRelease + ' )'"),
|
||||
),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user