diff --git a/pkg/handlers/contact.go b/pkg/handlers/contact.go index a7d79af..632710a 100644 --- a/pkg/handlers/contact.go +++ b/pkg/handlers/contact.go @@ -3,12 +3,15 @@ package handlers import ( "fmt" + "github.com/a-h/templ" "github.com/go-playground/validator/v10" "github.com/labstack/echo/v4" "git.grosinger.net/tgrosinger/saasitone/pkg/form" "git.grosinger.net/tgrosinger/saasitone/pkg/page" "git.grosinger.net/tgrosinger/saasitone/pkg/services" + "git.grosinger.net/tgrosinger/saasitone/templ/layouts" + "git.grosinger.net/tgrosinger/saasitone/templ/pages" "git.grosinger.net/tgrosinger/saasitone/templates" ) @@ -22,13 +25,6 @@ type ( mail *services.MailClient *services.TemplateRenderer } - - contactForm struct { - Email string `form:"email" validate:"required,email"` - Department string `form:"department" validate:"required,oneof=sales marketing hr"` - Message string `form:"message" validate:"required"` - form.Submission - } ) func init() { @@ -48,16 +44,22 @@ func (h *Contact) Routes(g *echo.Group) { func (h *Contact) Page(ctx echo.Context) error { p := page.New(ctx) - p.Layout = templates.LayoutMain p.Name = templates.PageContact p.Title = "Contact us" - p.Form = form.Get[contactForm](ctx) - return h.RenderPage(ctx, p) + f := form.Get[pages.ContactForm](ctx) + component := pages.Contact(p, f) + + // TODO: This can be reused + p.LayoutComponent = func(content templ.Component) templ.Component { + return layouts.Main(p, content) + } + + return h.RenderPageTempl(ctx, p, component) } func (h *Contact) Submit(ctx echo.Context) error { - var input contactForm + var input pages.ContactForm err := form.Submit(ctx, &input) diff --git a/templ/pages/contact.templ b/templ/pages/contact.templ new file mode 100644 index 0000000..390df9a --- /dev/null +++ b/templ/pages/contact.templ @@ -0,0 +1,74 @@ +package pages + +import ( + "git.grosinger.net/tgrosinger/saasitone/templ/components" + "git.grosinger.net/tgrosinger/saasitone/pkg/form" + "git.grosinger.net/tgrosinger/saasitone/pkg/page" +) + +type ContactForm struct { + Email string `form:"email" validate:"required,email"` + Department string `form:"department" validate:"required,oneof=sales marketing hr"` + Message string `form:"message" validate:"required"` + form.Submission +} + +templ Contact(p page.Page, f *ContactForm) { + if p.HTMX.Request.Target != "contact" { + + } + if f.IsDone() { +
+
+

Thank you!

+
+
+ No email was actually sent but this entire operation was handled server-side and degrades without JavaScript enabled. +
+
+ } else { +
+
+ +
+ +
+ @components.FieldErrors(f.Submission.GetFieldErrors("Email")) +
+
+ + + + + @components.FieldErrors(f.Submission.GetFieldErrors("Department")) +
+
+ +
+ +
+ @components.FieldErrors(f.Submission.GetFieldErrors("Message")) +
+
+
+ +
+
+ @components.CSRF(p.CSRF) +
+ } +} diff --git a/templ/pages/contact_templ.go b/templ/pages/contact_templ.go new file mode 100644 index 0000000..cd7787d --- /dev/null +++ b/templ/pages/contact_templ.go @@ -0,0 +1,206 @@ +// Code generated by templ - DO NOT EDIT. + +// templ: version: v0.2.707 +package pages + +//lint:file-ignore SA4006 This context is only used if a nested component is present. + +import "github.com/a-h/templ" +import "context" +import "io" +import "bytes" + +import ( + "git.grosinger.net/tgrosinger/saasitone/pkg/form" + "git.grosinger.net/tgrosinger/saasitone/pkg/page" + "git.grosinger.net/tgrosinger/saasitone/templ/components" +) + +type ContactForm struct { + Email string `form:"email" validate:"required,email"` + Department string `form:"department" validate:"required,oneof=sales marketing hr"` + Message string `form:"message" validate:"required"` + form.Submission +} + +func Contact(p page.Page, f *ContactForm) templ.Component { + return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) { + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer) + if !templ_7745c5c3_IsBuffer { + templ_7745c5c3_Buffer = templ.GetBuffer() + defer templ.ReleaseBuffer(templ_7745c5c3_Buffer) + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var1 := templ.GetChildren(ctx) + if templ_7745c5c3_Var1 == nil { + templ_7745c5c3_Var1 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + if p.HTMX.Request.Target != "contact" { + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

This is an example of a form with inline, server-side validation and HTMX-powered AJAX submissions without writing a single line of JavaScript.

Only the form below will update async upon submission.

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + if f.IsDone() { + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

Thank you!

No email was actually sent but this entire operation was handled server-side and degrades without JavaScript enabled.
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } else { + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var3 = []any{"input", f.Submission.GetFieldStatusClass("Email")} + templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var3...) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = components.FieldErrors(f.Submission.GetFieldErrors("Email")).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = components.FieldErrors(f.Submission.GetFieldErrors("Department")).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var6 = []any{"textarea", f.Submission.GetFieldStatusClass("Message")} + templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var6...) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = components.FieldErrors(f.Submission.GetFieldErrors("Message")).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = components.CSRF(p.CSRF).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + if !templ_7745c5c3_IsBuffer { + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W) + } + return templ_7745c5c3_Err + }) +} diff --git a/templates/pages/contact.gohtml b/templates/pages/contact.gohtml deleted file mode 100644 index 26f82d0..0000000 --- a/templates/pages/contact.gohtml +++ /dev/null @@ -1,68 +0,0 @@ -{{define "content"}} - {{- if not (eq .HTMX.Request.Target "contact")}} - - {{- end}} - - {{template "form" .}} -{{end}} - -{{define "form"}} - {{- if .Form.IsDone}} -
-
-

Thank you!

-
-
- No email was actually sent but this entire operation was handled server-side and degrades without JavaScript enabled. -
-
- {{- else}} -
-
- -
- -
- {{template "field-errors" (.Form.GetFieldErrors "Email")}} -
- -
- - - - - {{template "field-errors" (.Form.GetFieldErrors "Department")}} -
- -
- -
- -
- {{template "field-errors" (.Form.GetFieldErrors "Message")}} -
- -
-
- -
-
- - {{template "csrf" .}} -
- {{- end}} -{{end}} \ No newline at end of file