diff --git a/config/eiffel.toml b/config/eiffel.toml index 5f606d0e32a1341c50e2e8ac2819adc0f6c12809..23de56ef7fc0008f960c40889935919471c13bb9 100644 --- a/config/eiffel.toml +++ b/config/eiffel.toml @@ -1,2 +1 @@ -[output] -base_dir = "files/eiffel" \ No newline at end of file +neglect_optional = true \ No newline at end of file diff --git a/public/assets/css/styles.css b/public/assets/css/styles.css index fd395eb49572759ad661bc5ad1cd5c89c7f8d6c7..c928b864ef23ad7b1064b58b7ce9105fd8e0369e 100644 --- a/public/assets/css/styles.css +++ b/public/assets/css/styles.css @@ -20,4 +20,13 @@ .eiffel-requirements-list-item:hover { background-color: rgba(var(--bs-light-rgb), 0.1); +} + +#eiffelElicitationForm.eiffel-neglect-optional input, #eiffelElicitationForm.eiffel-neglect-optional textarea { + border-color: var(--bs-gray-700); +} + +#eiffelElicitationForm.eiffel-neglect-optional :optional { + border-color: var(--bs-gray-400); + opacity: 0.75; } \ No newline at end of file diff --git a/src/app/eiffel/eiffel.go b/src/app/eiffel/eiffel.go index bb7d93734b0d2a9a3d8487120f5456880d602a6d..087574c0f5fdd0d8b5f748b23dd9c4c4bbcdd882 100644 --- a/src/app/eiffel/eiffel.go +++ b/src/app/eiffel/eiffel.go @@ -3,7 +3,7 @@ package eiffel // Cfg is EIFFEL's configuration struct. This can be used to unmarshal a TOML configuration file into. type Cfg struct { - Output OutputCfg `toml:"output"` + NeglectOptional bool `toml:"neglect_optional"` } // TODO add tests for service, web and output diff --git a/src/app/eiffel/web.go b/src/app/eiffel/web.go index e3e2a721f145874bd5875ea1f2db8184e885bfa1..52fc4383b7ec55a8deb705aa4deedfce4f770285 100644 --- a/src/app/eiffel/web.go +++ b/src/app/eiffel/web.go @@ -61,6 +61,8 @@ type TemplateFormData struct { // SegmentMap is a map of rule names to their corresponding segment value. // This is used to fill the segments with their values after parsing. SegmentMap map[string]string + // NeglectOptional is a flag indicating if optional rules (inputs) should be displayed different from non-optional rules. + NeglectOptional bool } // SearchTemplateData contains templates to render as search results and a flag indicating if the query was too short. @@ -85,14 +87,14 @@ func RegisterController(appCtx *hctx.AppCtx, webCtx *web.Ctx) { router := webCtx.Router.With(user.LoggedInMiddleware(appCtx)) - router.Get("/eiffel", eiffelElicitationPage(appCtx, webCtx).ServeHTTP) - router.Get("/eiffel/{templateID}", eiffelElicitationPage(appCtx, webCtx).ServeHTTP) - router.Get("/eiffel/{templateID}/{variant}", eiffelElicitationPage(appCtx, webCtx).ServeHTTP) + router.Get("/eiffel", eiffelElicitationPage(cfg, appCtx, webCtx).ServeHTTP) + router.Get("/eiffel/{templateID}", eiffelElicitationPage(cfg, appCtx, webCtx).ServeHTTP) + router.Get("/eiffel/{templateID}/{variant}", eiffelElicitationPage(cfg, appCtx, webCtx).ServeHTTP) router.Get("/eiffel/elicitation/templates/search/modal", searchModal(appCtx, webCtx).ServeHTTP) router.Post("/eiffel/elicitation/templates/search", searchTemplate(appCtx, webCtx).ServeHTTP) - router.Get("/eiffel/elicitation/{templateID}", elicitationTemplate(appCtx, webCtx, true).ServeHTTP) - router.Get("/eiffel/elicitation/{templateID}/{variant}", elicitationTemplate(appCtx, webCtx, false).ServeHTTP) - router.Post("/eiffel/elicitation/{templateID}/{variant}", parseRequirement(appCtx, webCtx, cfg).ServeHTTP) + router.Get("/eiffel/elicitation/{templateID}", elicitationTemplate(cfg, appCtx, webCtx, true).ServeHTTP) + router.Get("/eiffel/elicitation/{templateID}/{variant}", elicitationTemplate(cfg, appCtx, webCtx, false).ServeHTTP) + router.Post("/eiffel/elicitation/{templateID}/{variant}", parseRequirement(cfg, appCtx, webCtx).ServeHTTP) } func subscribeEvents(appCtx *hctx.AppCtx) { @@ -139,7 +141,7 @@ func registerNavigation(appCtx *hctx.AppCtx, webCtx *web.Ctx) { }) } -func eiffelElicitationPage(appCtx *hctx.AppCtx, webCtx *web.Ctx) http.Handler { +func eiffelElicitationPage(cfg Cfg, appCtx *hctx.AppCtx, webCtx *web.Ctx) http.Handler { templateRepository := util.UnwrapType[template.Repository](appCtx.Repository(template.RepositoryName)) sessionStore := util.UnwrapType[user.SessionRepository](appCtx.Repository(user.SessionRepositoryName)) @@ -147,7 +149,7 @@ func eiffelElicitationPage(appCtx *hctx.AppCtx, webCtx *web.Ctx) http.Handler { templateID := web.URLParam(io.Request(), "templateID") variantKey := web.URLParam(io.Request(), "variant") if templateID == "" { - return renderElicitationPage(io, TemplateFormData{}, nil, nil) + return renderElicitationPage(io, TemplateFormData{NeglectOptional: cfg.NeglectOptional}, nil, nil) } formData, err := TemplateFormFromRequest( @@ -160,6 +162,7 @@ func eiffelElicitationPage(appCtx *hctx.AppCtx, webCtx *web.Ctx) http.Handler { true, ) + formData.NeglectOptional = cfg.NeglectOptional formData.CopyAfterParse = CopyAfterParseSetting(io.Request(), sessionStore, true) return renderElicitationPage(io, formData, nil, []error{err}) @@ -216,7 +219,7 @@ func searchTemplate(appCtx *hctx.AppCtx, webCtx *web.Ctx) http.Handler { }) } -func elicitationTemplate(appCtx *hctx.AppCtx, webCtx *web.Ctx, defaultFirstVariant bool) http.Handler { +func elicitationTemplate(cfg Cfg, appCtx *hctx.AppCtx, webCtx *web.Ctx, defaultFirstVariant bool) http.Handler { templateRepository := util.UnwrapType[template.Repository](appCtx.Repository(template.RepositoryName)) sessionStore := util.UnwrapType[user.SessionRepository](appCtx.Repository(user.SessionRepositoryName)) @@ -239,6 +242,7 @@ func elicitationTemplate(appCtx *hctx.AppCtx, webCtx *web.Ctx, defaultFirstVaria return io.InlineError(err) } + formData.NeglectOptional = cfg.NeglectOptional formData.CopyAfterParse = CopyAfterParseSetting(io.Request(), sessionStore, true) io.Response().Header().Set("HX-Push-URL", fmt.Sprintf("/eiffel/%s/%s", templateID, formData.VariantKey)) @@ -252,7 +256,7 @@ func elicitationTemplate(appCtx *hctx.AppCtx, webCtx *web.Ctx, defaultFirstVaria }) } -func parseRequirement(appCtx *hctx.AppCtx, webCtx *web.Ctx, cfg Cfg) http.Handler { +func parseRequirement(cfg Cfg, appCtx *hctx.AppCtx, webCtx *web.Ctx) http.Handler { templateRepository := util.UnwrapType[template.Repository](appCtx.Repository(template.RepositoryName)) sessionStore := util.UnwrapType[user.SessionRepository](appCtx.Repository(user.SessionRepositoryName)) @@ -305,6 +309,7 @@ func parseRequirement(appCtx *hctx.AppCtx, webCtx *web.Ctx, cfg Cfg) http.Handle io.Response().Header().Set("ParsingSuccessEvent", base64.URLEncoding.EncodeToString(triggerEventJSON)) } + formData.NeglectOptional = cfg.NeglectOptional formData.CopyAfterParse = CopyAfterParseSetting(request, sessionStore, false) return io.Render(web.NewFormData(formData, s, err), "eiffel.elicitation.form", "eiffel/_form-elicitation.go.html") diff --git a/templates/eiffel/_form-elicitation.go.html b/templates/eiffel/_form-elicitation.go.html index f52c6e9f73013b3747087fef3dece50d78158828..cd4c9fb80bffd29af8f89d4d53a0edc73ebe6b1d 100644 --- a/templates/eiffel/_form-elicitation.go.html +++ b/templates/eiffel/_form-elicitation.go.html @@ -9,7 +9,8 @@ hx-target=".eiffel-elicitation-template-variant-form" hx-disabled-elt=".eiffel-elicitation-form-fieldset" autocomplete="off" - id="eiffelElicitationForm"> + id="eiffelElicitationForm" + {{ if .Data.Form.NeglectOptional }}class="eiffel-neglect-optional"{{ end }}> <fieldset class="eiffel-elicitation-form-fieldset"> <div class="row"> {{/* TODO beautify this code and improve readability - good templating is hard :/ */}}