feat(sketch): create new sketch form

closes: #23
master
Niclas Thobaben 2024-03-03 09:26:07 +01:00
parent 693748ee37
commit 3ccb44f6df
9 changed files with 241 additions and 18 deletions

View File

@ -17,8 +17,8 @@ body:
options:
- Artist
- Administrator
- Public User
- Developer
- User
placeholder: ...role...
validations:
required: true

View File

@ -1,6 +1,11 @@
package artist
import "github.com/nicksnyder/go-i18n/v2/i18n"
import (
"errors"
"git.l--n.de/nclazz/soundwerft/api/handler"
"git.l--n.de/nclazz/soundwerft/internal/app/sketchbook"
"github.com/nicksnyder/go-i18n/v2/i18n"
)
var (
MsgCreateSketchbookTitle = &i18n.Message{
@ -16,4 +21,45 @@ var (
ID: "Artist.ErrCreateSketchbook",
Other: "Failed to create sketchbook. Check your input and try again",
}
MsgCreateSketchTitle = &i18n.Message{
ID: "Artist.CreateSketchTitle",
Other: "Create new sketch",
}
MsgErrCreateSketch = &i18n.Message{
ID: "Artist.ErrCreateSketch",
Other: "Failed to create sketch. Please check your input and try again",
}
MsgSketchTitleLabel = &i18n.Message{
ID: "Artist.SketchTitleLabel",
Other: "Title",
}
MsgSketchDescriptionLabel = &i18n.Message{
ID: "Artist.SketchDescriptionLabel",
Other: "Description",
}
MsgSketchCreate = &i18n.Message{
ID: "Artist.SketchCreate",
Other: "Create",
}
MsgErrSketchTitleTooLong = &i18n.Message{
ID: "Artist.SketchTitleTooLong",
Other: "Exceeds maximum length",
}
MsgErrSketchDescriptionTooLong = &i18n.Message{
ID: "Artist.SketchDescriptionTooLong",
Other: "Exceeds maximum length",
}
)
func TranslateError(h *handler.Handler, err error) string {
if err == nil {
return ""
}
if errors.Is(err, sketchbook.ErrTitleTooLong) {
return h.TranslateMessage(MsgErrSketchTitleTooLong)
}
if errors.Is(err, sketchbook.ErrDescriptionTooLong) {
return h.TranslateMessage(MsgErrSketchDescriptionTooLong)
}
return err.Error()
}

View File

@ -14,6 +14,11 @@ func Routes(h *handler.Handler) {
g := h.Echo.Group("/sketchbook", guard.Authenticated(h))
g.POST("", postCreateSketchbook(h))
g = h.Echo.Group("/sketch", guard.Authenticated(h))
g.GET("/new", getCreateSketchPage(h))
g.POST("", postCreateSketch(h))
g.POST("/", postCreateSketch(h))
}
func artistView(c echo.Context, h *handler.Handler) artist.ArtistView {

View File

@ -0,0 +1,65 @@
package artist
import (
"errors"
"git.l--n.de/nclazz/soundwerft/api/handler"
"git.l--n.de/nclazz/soundwerft/api/web/templates/artist"
"git.l--n.de/nclazz/soundwerft/internal/app/sketchbook"
"git.l--n.de/nclazz/soundwerft/pkg/errx"
"github.com/labstack/echo/v4"
"log/slog"
)
func createSketchFormView(c echo.Context, h *handler.Handler) artist.CreateSketchFormView {
return artist.CreateSketchFormView{
MsgTitleLabel: h.TranslateMessage(MsgSketchTitleLabel),
MsgDescriptionLabel: h.TranslateMessage(MsgSketchDescriptionLabel),
MsgTitle: h.TranslateMessage(MsgCreateSketchTitle),
MsgCreate: h.TranslateMessage(MsgSketchCreate),
}
}
func getCreateSketchPage(h *handler.Handler) echo.HandlerFunc {
return func(c echo.Context) error {
return h.Render(c, artist.CreateSketch(h.View(c), createSketchFormView(c, h)))
}
}
func postCreateSketch(h *handler.Handler) echo.HandlerFunc {
return func(c echo.Context) error {
ctx := c.Request().Context()
usr := h.User(c)
sk, err := h.Repos.Sketchbooks.GetByOwnerID(ctx, usr.ID)
if err != nil {
return err
}
cmd := sketchbook.CreateSketchCommand{
Sketchbook: sk.ID.String(),
Title: c.FormValue("title"),
Description: c.FormValue("description"),
}
view := createSketchFormView(c, h)
view.Title = cmd.Title
view.Description = cmd.Description
log := h.Logger(c).With(slog.Group("sketch", "title", cmd.Title, "sketchbook", sk.ID))
log.Info("create sketch")
sketch, err := h.Services.Sketchbooks.CreateSketch(ctx, cmd)
if err != nil {
log.Warn("create sketch failed", "error", err)
view.Err = h.TranslateMessage(MsgErrCreateSketch)
mapCreateSketchErrs(h, &view, err)
return h.Render(c, artist.CreateSketchForm(h.View(c), view))
}
log.Info("created sketch", "id", sketch.ID)
return h.Render(c, artist.Dashboard())
}
}
func mapCreateSketchErrs(h *handler.Handler, view *artist.CreateSketchFormView, err error) {
var errs errx.ErrMap
if errors.As(err, &errs) {
view.TitleErr = TranslateError(h, errs.Get("title"))
view.DescriptionErr = TranslateError(h, errs.Get("description"))
return
}
}

View File

@ -68,7 +68,7 @@ func postLogin(h *handler.Handler) echo.HandlerFunc {
if err := h.SaveSession(c); err != nil {
return err
}
return h.HardRedirect(c, h.View(c).URLString("/artist"))
return h.HardRedirect(c, h.View(c).URLString("/home"))
}
}

View File

@ -0,0 +1,75 @@
package artist
import (
"git.l--n.de/nclazz/soundwerft/api/web"
"git.l--n.de/nclazz/soundwerft/api/web/templates/partials"
)
type CreateSketchFormView struct {
Title string
Description string
TitleErr string
DescriptionErr string
Err string
MsgTitleLabel string
MsgDescriptionLabel string
MsgTitle string
MsgCreate string
}
templ CreateSketch(view web.View, form CreateSketchFormView) {
@partials.Document(view) {
@CreateSketchForm(view, form)
}
}
templ CreateSketchForm(view web.View, form CreateSketchFormView) {
<div class="container-fluid">
<div class="row justify-content-center">
<div class="col-md-3">
<h4>{ form.MsgTitle }</h4>
</div>
</div>
<div class="row justify-content-center">
<div class="col-md-3">
<form class="form"
method="post"
action={view.URL("/sketch")}
hx-boost="true"
hx-target="#main"
hx-replace-url="false">
if form.Err != "" {
@partials.Alert("danger") {
{ form.Err }
}
}
@partials.FormInput(partials.InputAttrs{
ID: "sketch_title",
Name: "title",
Label: form.MsgTitleLabel,
Placeholder: form.MsgTitleLabel,
Required: true,
Value: form.Title,
HasErr: form.TitleErr != "",
Err: form.TitleErr,
})
@partials.FormInput(partials.InputAttrs{
ID: "sketch_description",
Name: "description",
Type: "textarea",
Label: form.MsgDescriptionLabel,
Placeholder: form.MsgDescriptionLabel,
Value: form.Description,
HasErr: form.DescriptionErr != "",
Err: form.DescriptionErr,
})
<div class="d-grid gap-2">
<input type="submit" class="btn btn-primary btn-lg" value={ form.MsgCreate }/>
</div>
</form>
</div>
</div>
</div>
}

View File

@ -17,6 +17,19 @@ type CreateSketchbookFormView struct {
templ ArtistPage(view web.View, artistView ArtistView, form CreateSketchbookFormView) {
@partials.Document(view) {
<div class="container">
<div class="row">
<div class="col">
<a class="btn btn-primary"
href={ view.URL("/sketch/new") }
hx-boost="true"
hx-select="#main"
hx-target="#main">
Create Sketch
</a>
</div>
</div>
</div>
if !artistView.HasSketchbook {
@CreateSketchbookForm(view, form)
} else {
@ -60,4 +73,3 @@ templ CreateSketchbookForm(view web.View, form CreateSketchbookFormView) {
</div>
</div>
}

View File

@ -26,20 +26,24 @@ templ FormInput(attrs InputAttrs) {
<div class="mb-3">
<label for={ attrs.InputID() } class="form-label">{ attrs.Label }</label>
<div class="input-group has-validation">
<input id={ attrs.InputID() }
name={ attrs.Name }
if attrs.Type != "" {
type={ attrs.Type }
}
if attrs.Placeholder != "" {
placeholder={ attrs.Placeholder }
}
if attrs.Value != "" {
value={ attrs.Value }
}
required?={ attrs.Required }
class={ "form-control", templ.KV("is-invalid", attrs.HasErr), templ.KV("is-valid", attrs.Success != "" || attrs.HasSuccess) }
/>
if attrs.Type == "textarea" {
@textArea(attrs)
} else {
<input id={ attrs.InputID() }
name={ attrs.Name }
if attrs.Type != "" {
type={ attrs.Type }
}
if attrs.Placeholder != "" {
placeholder={ attrs.Placeholder }
}
if attrs.Value != "" {
value={ attrs.Value }
}
required?={ attrs.Required }
class={ "form-control", templ.KV("is-invalid", attrs.HasErr), templ.KV("is-valid", attrs.Success != "" || attrs.HasSuccess) }
/>
}
{children...}
if attrs.Success != "" {
<div class="valid-feedback">
@ -58,6 +62,16 @@ templ FormInput(attrs InputAttrs) {
</div>
}
templ textArea(attrs InputAttrs) {
<textarea id={attrs.ID}
name={attrs.Name}
placeholder={attrs.Placeholder}
required?={attrs.Required}
style="height: 150px"
class={ "form-control", templ.KV("is-invalid", attrs.HasErr), templ.KV("is-valid", attrs.Success != "" || attrs.HasSuccess) }>
{attrs.Value}</textarea>
}
templ InputGroup() {
<div class="input-group mb-3">
{children...}

View File

@ -1,6 +1,12 @@
"Artist.CreateSketchTitle" = "Create new sketch"
"Artist.CreateSketchbook" = "Create"
"Artist.CreateSketchbookTitle" = "Set up your Sketchbook"
"Artist.ErrCreateSketchbook" = "Failed to create sketchbook. Check your input and try again"
"Artist.SketchCreate" = "Create"
"Artist.SketchDescriptionLabel" = "Description"
"Artist.SketchDescriptionTooLong" = "Exceeds maximum length"
"Artist.SketchTitleLabel" = "Title"
"Artist.SketchTitleTooLong" = "Exceeds maximum length"
Save = "Save"
"User.AlreadyHaveAccount" = "Already have an account?"
"User.ConfirmPasswordLabel" = "Confirm password"