Allow optional select with empty label

This is not yet necessary, but the empty label is because i do not want
to select a default tax for products—at least, not without a setting for
it.

Since i need to add the required attribute now to select, because
otherwise the browser would allow sending that empty value, i did not
want to do it unconditionally, just in case.
This commit is contained in:
jordi fita mas 2023-02-05 14:06:33 +01:00
parent 60f9792e58
commit ae1949024b
9 changed files with 100 additions and 79 deletions

View File

@ -88,6 +88,7 @@ func newTaxDetailsForm(ctx context.Context, conn *Conn, locale *Locale) *taxDeta
Name: "currency", Name: "currency",
Label: pgettext("input", "Currency", locale), Label: pgettext("input", "Currency", locale),
Options: MustGetOptions(ctx, conn, "select currency_code, currency_symbol from currency order by currency_code"), Options: MustGetOptions(ctx, conn, "select currency_code, currency_symbol from currency order by currency_code"),
Required: true,
Selected: "EUR", Selected: "EUR",
}, },
} }

View File

@ -224,6 +224,7 @@ func newContactForm(ctx context.Context, conn *Conn, locale *Locale) *contactFor
Name: "country", Name: "country",
Label: pgettext("input", "Tax", locale), Label: pgettext("input", "Tax", locale),
Options: mustGetCountryOptions(ctx, conn, locale), Options: mustGetCountryOptions(ctx, conn, locale),
Required: true,
Selected: "ES", Selected: "ES",
Attributes: []template.HTMLAttr{ Attributes: []template.HTMLAttr{
`autocomplete="country"`, `autocomplete="country"`,

View File

@ -61,6 +61,8 @@ type SelectField struct {
Selected string Selected string
Options []*SelectOption Options []*SelectOption
Attributes []template.HTMLAttr Attributes []template.HTMLAttr
Required bool
EmptyLabel string
Errors []error Errors []error
} }

View File

@ -36,6 +36,7 @@ func GetProductForm(w http.ResponseWriter, r *http.Request, params httprouter.Pa
form := newProductForm(r.Context(), conn, locale, company) form := newProductForm(r.Context(), conn, locale, company)
slug := params[0].Value slug := params[0].Value
if slug == "new" { if slug == "new" {
form.Tax.EmptyLabel = gettext("Select a tax for this product.", locale)
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
mustRenderNewProductForm(w, r, form) mustRenderNewProductForm(w, r, form)
return return
@ -166,6 +167,7 @@ func newProductForm(ctx context.Context, conn *Conn, locale *Locale, company *Co
Tax: &SelectField{ Tax: &SelectField{
Name: "tax", Name: "tax",
Label: pgettext("input", "Tax", locale), Label: pgettext("input", "Tax", locale),
Required: true,
Options: MustGetOptions(ctx, conn, "select tax_id::text, name from tax where company_id = $1 order by name", company.Id), Options: MustGetOptions(ctx, conn, "select tax_id::text, name from tax where company_id = $1 order by name", company.Id),
}, },
} }

View File

@ -64,6 +64,7 @@ func newProfileForm(ctx context.Context, conn *Conn, locale *Locale) *profileFor
Name: "language", Name: "language",
Label: pgettext("input", "Language", locale), Label: pgettext("input", "Language", locale),
Options: languages, Options: languages,
Required: true,
Attributes: []template.HTMLAttr{ Attributes: []template.HTMLAttr{
`autocomplete="language"`, `autocomplete="language"`,
}, },

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: numerus\n" "Project-Id-Version: numerus\n"
"Report-Msgid-Bugs-To: jordi@tandem.blog\n" "Report-Msgid-Bugs-To: jordi@tandem.blog\n"
"POT-Creation-Date: 2023-02-04 11:24+0100\n" "POT-Creation-Date: 2023-02-05 14:04+0100\n"
"PO-Revision-Date: 2023-01-18 17:08+0100\n" "PO-Revision-Date: 2023-01-18 17:08+0100\n"
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n" "Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
"Language-Team: Catalan <ca@dodds.net>\n" "Language-Team: Catalan <ca@dodds.net>\n"
@ -238,11 +238,11 @@ msgctxt "input"
msgid "Password" msgid "Password"
msgstr "Contrasenya" msgstr "Contrasenya"
#: pkg/login.go:69 pkg/profile.go:88 pkg/contacts.go:262 #: pkg/login.go:69 pkg/profile.go:89 pkg/contacts.go:263
msgid "Email can not be empty." msgid "Email can not be empty."
msgstr "No podeu deixar el correu-e en blanc." msgstr "No podeu deixar el correu-e en blanc."
#: pkg/login.go:70 pkg/profile.go:89 pkg/contacts.go:263 #: pkg/login.go:70 pkg/profile.go:90 pkg/contacts.go:264
msgid "This value is not a valid email. It should be like name@domain.com." msgid "This value is not a valid email. It should be like name@domain.com."
msgstr "Aquest valor no és un correu-e vàlid. Hauria de ser similar a nom@domini.cat." msgstr "Aquest valor no és un correu-e vàlid. Hauria de ser similar a nom@domini.cat."
@ -254,70 +254,74 @@ msgstr "No podeu deixar la contrasenya en blanc."
msgid "Invalid user or password." msgid "Invalid user or password."
msgstr "Nom dusuari o contrasenya incorrectes." msgstr "Nom dusuari o contrasenya incorrectes."
#: pkg/products.go:144 #: pkg/products.go:39
msgid "Select a tax for this product."
msgstr "Escolliu un impost per aquest producte."
#: pkg/products.go:148
msgctxt "input" msgctxt "input"
msgid "Name" msgid "Name"
msgstr "Nom" msgstr "Nom"
#: pkg/products.go:150 #: pkg/products.go:154
msgctxt "input" msgctxt "input"
msgid "Description" msgid "Description"
msgstr "Descripció" msgstr "Descripció"
#: pkg/products.go:155 #: pkg/products.go:159
msgctxt "input" msgctxt "input"
msgid "Price" msgid "Price"
msgstr "Preu" msgstr "Preu"
#: pkg/products.go:164 pkg/contacts.go:225 #: pkg/products.go:169 pkg/contacts.go:225
msgctxt "input" msgctxt "input"
msgid "Tax" msgid "Tax"
msgstr "Impost" msgstr "Impost"
#: pkg/products.go:183 pkg/profile.go:91 #: pkg/products.go:189 pkg/profile.go:92
msgid "Name can not be empty." msgid "Name can not be empty."
msgstr "No podeu deixar el nom en blanc." msgstr "No podeu deixar el nom en blanc."
#: pkg/products.go:184 #: pkg/products.go:190
msgid "Price can not be empty." msgid "Price can not be empty."
msgstr "No podeu deixar el preu en blanc." msgstr "No podeu deixar el preu en blanc."
#: pkg/products.go:185 #: pkg/products.go:191
msgid "Price must be a number greater than zero." msgid "Price must be a number greater than zero."
msgstr "El preu ha de ser un número major a zero." msgstr "El preu ha de ser un número major a zero."
#: pkg/products.go:187 #: pkg/products.go:193
msgid "Selected tax is not valid." msgid "Selected tax is not valid."
msgstr "Heu seleccionat un impost que no és vàlid." msgstr "Heu seleccionat un impost que no és vàlid."
#: pkg/company.go:78 #: pkg/company.go:89
msgctxt "input" msgctxt "input"
msgid "Currency" msgid "Currency"
msgstr "Moneda" msgstr "Moneda"
#: pkg/company.go:95 #: pkg/company.go:107
msgid "Selected currency is not valid." msgid "Selected currency is not valid."
msgstr "Heu seleccionat una moneda que no és vàlida." msgstr "Heu seleccionat una moneda que no és vàlida."
#: pkg/company.go:217 #: pkg/company.go:229
msgctxt "input" msgctxt "input"
msgid "Tax name" msgid "Tax name"
msgstr "Nom impost" msgstr "Nom impost"
#: pkg/company.go:223 #: pkg/company.go:235
msgctxt "input" msgctxt "input"
msgid "Rate (%)" msgid "Rate (%)"
msgstr "Percentatge" msgstr "Percentatge"
#: pkg/company.go:245 #: pkg/company.go:257
msgid "Tax name can not be empty." msgid "Tax name can not be empty."
msgstr "No podeu deixar el nom de limpost en blanc." msgstr "No podeu deixar el nom de limpost en blanc."
#: pkg/company.go:246 #: pkg/company.go:258
msgid "Tax rate can not be empty." msgid "Tax rate can not be empty."
msgstr "No podeu deixar percentatge en blanc." msgstr "No podeu deixar percentatge en blanc."
#: pkg/company.go:247 #: pkg/company.go:259
msgid "Tax rate must be an integer between -99 and 99." msgid "Tax rate must be an integer between -99 and 99."
msgstr "El percentatge ha de ser entre -99 i 99." msgstr "El percentatge ha de ser entre -99 i 99."
@ -341,11 +345,11 @@ msgctxt "input"
msgid "Language" msgid "Language"
msgstr "Idioma" msgstr "Idioma"
#: pkg/profile.go:92 #: pkg/profile.go:93
msgid "Confirmation does not match password." msgid "Confirmation does not match password."
msgstr "La confirmació no és igual a la contrasenya." msgstr "La confirmació no és igual a la contrasenya."
#: pkg/profile.go:93 #: pkg/profile.go:94
msgid "Selected language is not valid." msgid "Selected language is not valid."
msgstr "Heu seleccionat un idioma que no és vàlid." msgstr "Heu seleccionat un idioma que no és vàlid."
@ -394,51 +398,51 @@ msgctxt "input"
msgid "Postal code" msgid "Postal code"
msgstr "Codi postal" msgstr "Codi postal"
#: pkg/contacts.go:255 #: pkg/contacts.go:256
msgid "Business name can not be empty." msgid "Business name can not be empty."
msgstr "No podeu deixar el nom i els cognoms en blanc." msgstr "No podeu deixar el nom i els cognoms en blanc."
#: pkg/contacts.go:256 #: pkg/contacts.go:257
msgid "VAT number can not be empty." msgid "VAT number can not be empty."
msgstr "No podeu deixar el DNI o NIF en blanc." msgstr "No podeu deixar el DNI o NIF en blanc."
#: pkg/contacts.go:257 #: pkg/contacts.go:258
msgid "This value is not a valid VAT number." msgid "This value is not a valid VAT number."
msgstr "Aquest valor no és un DNI o NIF vàlid." msgstr "Aquest valor no és un DNI o NIF vàlid."
#: pkg/contacts.go:259 #: pkg/contacts.go:260
msgid "Phone can not be empty." msgid "Phone can not be empty."
msgstr "No podeu deixar el telèfon en blanc." msgstr "No podeu deixar el telèfon en blanc."
#: pkg/contacts.go:260 #: pkg/contacts.go:261
msgid "This value is not a valid phone number." msgid "This value is not a valid phone number."
msgstr "Aquest valor no és un telèfon vàlid." msgstr "Aquest valor no és un telèfon vàlid."
#: pkg/contacts.go:266 #: pkg/contacts.go:267
msgid "This value is not a valid web address. It should be like https://domain.com/." msgid "This value is not a valid web address. It should be like https://domain.com/."
msgstr "Aquest valor no és una adreça web vàlida. Hauria de ser similar a https://domini.cat/." msgstr "Aquest valor no és una adreça web vàlida. Hauria de ser similar a https://domini.cat/."
#: pkg/contacts.go:268 #: pkg/contacts.go:269
msgid "Address can not be empty." msgid "Address can not be empty."
msgstr "No podeu deixar ladreça en blanc." msgstr "No podeu deixar ladreça en blanc."
#: pkg/contacts.go:269 #: pkg/contacts.go:270
msgid "City can not be empty." msgid "City can not be empty."
msgstr "No podeu deixar la població en blanc." msgstr "No podeu deixar la població en blanc."
#: pkg/contacts.go:270 #: pkg/contacts.go:271
msgid "Province can not be empty." msgid "Province can not be empty."
msgstr "No podeu deixar la província en blanc." msgstr "No podeu deixar la província en blanc."
#: pkg/contacts.go:271 #: pkg/contacts.go:272
msgid "Postal code can not be empty." msgid "Postal code can not be empty."
msgstr "No podeu deixar el codi postal en blanc." msgstr "No podeu deixar el codi postal en blanc."
#: pkg/contacts.go:272 #: pkg/contacts.go:273
msgid "This value is not a valid postal code." msgid "This value is not a valid postal code."
msgstr "Aquest valor no és un codi postal vàlid." msgstr "Aquest valor no és un codi postal vàlid."
#: pkg/contacts.go:274 #: pkg/contacts.go:275
msgid "Selected country is not valid." msgid "Selected country is not valid."
msgstr "Heu seleccionat un país que no és vàlid." msgstr "Heu seleccionat un país que no és vàlid."

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: numerus\n" "Project-Id-Version: numerus\n"
"Report-Msgid-Bugs-To: jordi@tandem.blog\n" "Report-Msgid-Bugs-To: jordi@tandem.blog\n"
"POT-Creation-Date: 2023-02-04 11:24+0100\n" "POT-Creation-Date: 2023-02-05 14:04+0100\n"
"PO-Revision-Date: 2023-01-18 17:45+0100\n" "PO-Revision-Date: 2023-01-18 17:45+0100\n"
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n" "Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
"Language-Team: Spanish <es@tp.org.es>\n" "Language-Team: Spanish <es@tp.org.es>\n"
@ -238,11 +238,11 @@ msgctxt "input"
msgid "Password" msgid "Password"
msgstr "Contraseña" msgstr "Contraseña"
#: pkg/login.go:69 pkg/profile.go:88 pkg/contacts.go:262 #: pkg/login.go:69 pkg/profile.go:89 pkg/contacts.go:263
msgid "Email can not be empty." msgid "Email can not be empty."
msgstr "No podéis dejar el correo-e en blanco." msgstr "No podéis dejar el correo-e en blanco."
#: pkg/login.go:70 pkg/profile.go:89 pkg/contacts.go:263 #: pkg/login.go:70 pkg/profile.go:90 pkg/contacts.go:264
msgid "This value is not a valid email. It should be like name@domain.com." msgid "This value is not a valid email. It should be like name@domain.com."
msgstr "Este valor no es un correo-e válido. Tiene que ser parecido a nombre@dominio.es." msgstr "Este valor no es un correo-e válido. Tiene que ser parecido a nombre@dominio.es."
@ -254,70 +254,74 @@ msgstr "No podéis dejar la contraseña en blanco."
msgid "Invalid user or password." msgid "Invalid user or password."
msgstr "Nombre de usuario o contraseña inválido." msgstr "Nombre de usuario o contraseña inválido."
#: pkg/products.go:144 #: pkg/products.go:39
msgid "Select a tax for this product."
msgstr "Escoged un impuesto para este producto."
#: pkg/products.go:148
msgctxt "input" msgctxt "input"
msgid "Name" msgid "Name"
msgstr "Nombre" msgstr "Nombre"
#: pkg/products.go:150 #: pkg/products.go:154
msgctxt "input" msgctxt "input"
msgid "Description" msgid "Description"
msgstr "Descripción" msgstr "Descripción"
#: pkg/products.go:155 #: pkg/products.go:159
msgctxt "input" msgctxt "input"
msgid "Price" msgid "Price"
msgstr "Precio" msgstr "Precio"
#: pkg/products.go:164 pkg/contacts.go:225 #: pkg/products.go:169 pkg/contacts.go:225
msgctxt "input" msgctxt "input"
msgid "Tax" msgid "Tax"
msgstr "Impuesto" msgstr "Impuesto"
#: pkg/products.go:183 pkg/profile.go:91 #: pkg/products.go:189 pkg/profile.go:92
msgid "Name can not be empty." msgid "Name can not be empty."
msgstr "No podéis dejar el nombre en blanco." msgstr "No podéis dejar el nombre en blanco."
#: pkg/products.go:184 #: pkg/products.go:190
msgid "Price can not be empty." msgid "Price can not be empty."
msgstr "No podéis dejar el precio en blanco." msgstr "No podéis dejar el precio en blanco."
#: pkg/products.go:185 #: pkg/products.go:191
msgid "Price must be a number greater than zero." msgid "Price must be a number greater than zero."
msgstr "El precio tiene que ser un número mayor a cero." msgstr "El precio tiene que ser un número mayor a cero."
#: pkg/products.go:187 #: pkg/products.go:193
msgid "Selected tax is not valid." msgid "Selected tax is not valid."
msgstr "Habéis escogido un impuesto que no es válido." msgstr "Habéis escogido un impuesto que no es válido."
#: pkg/company.go:78 #: pkg/company.go:89
msgctxt "input" msgctxt "input"
msgid "Currency" msgid "Currency"
msgstr "Moneda" msgstr "Moneda"
#: pkg/company.go:95 #: pkg/company.go:107
msgid "Selected currency is not valid." msgid "Selected currency is not valid."
msgstr "Habéis escogido una moneda que no es válida." msgstr "Habéis escogido una moneda que no es válida."
#: pkg/company.go:217 #: pkg/company.go:229
msgctxt "input" msgctxt "input"
msgid "Tax name" msgid "Tax name"
msgstr "Nombre impuesto" msgstr "Nombre impuesto"
#: pkg/company.go:223 #: pkg/company.go:235
msgctxt "input" msgctxt "input"
msgid "Rate (%)" msgid "Rate (%)"
msgstr "Porcentaje" msgstr "Porcentaje"
#: pkg/company.go:245 #: pkg/company.go:257
msgid "Tax name can not be empty." msgid "Tax name can not be empty."
msgstr "No podéis dejar el nombre del impuesto en blanco." msgstr "No podéis dejar el nombre del impuesto en blanco."
#: pkg/company.go:246 #: pkg/company.go:258
msgid "Tax rate can not be empty." msgid "Tax rate can not be empty."
msgstr "No podéis dejar el porcentaje en blanco." msgstr "No podéis dejar el porcentaje en blanco."
#: pkg/company.go:247 #: pkg/company.go:259
msgid "Tax rate must be an integer between -99 and 99." msgid "Tax rate must be an integer between -99 and 99."
msgstr "El porcentaje tiene que estar entre -99 y 99." msgstr "El porcentaje tiene que estar entre -99 y 99."
@ -341,11 +345,11 @@ msgctxt "input"
msgid "Language" msgid "Language"
msgstr "Idioma" msgstr "Idioma"
#: pkg/profile.go:92 #: pkg/profile.go:93
msgid "Confirmation does not match password." msgid "Confirmation does not match password."
msgstr "La confirmación no corresponde con la contraseña." msgstr "La confirmación no corresponde con la contraseña."
#: pkg/profile.go:93 #: pkg/profile.go:94
msgid "Selected language is not valid." msgid "Selected language is not valid."
msgstr "Habéis escogido un idioma que no es válido." msgstr "Habéis escogido un idioma que no es válido."
@ -394,51 +398,51 @@ msgctxt "input"
msgid "Postal code" msgid "Postal code"
msgstr "Código postal" msgstr "Código postal"
#: pkg/contacts.go:255 #: pkg/contacts.go:256
msgid "Business name can not be empty." msgid "Business name can not be empty."
msgstr "No podéis dejar el nombre y los apellidos en blanco." msgstr "No podéis dejar el nombre y los apellidos en blanco."
#: pkg/contacts.go:256 #: pkg/contacts.go:257
msgid "VAT number can not be empty." msgid "VAT number can not be empty."
msgstr "No podéis dejar el DNI o NIF en blanco." msgstr "No podéis dejar el DNI o NIF en blanco."
#: pkg/contacts.go:257 #: pkg/contacts.go:258
msgid "This value is not a valid VAT number." msgid "This value is not a valid VAT number."
msgstr "Este valor no es un DNI o NIF válido." msgstr "Este valor no es un DNI o NIF válido."
#: pkg/contacts.go:259 #: pkg/contacts.go:260
msgid "Phone can not be empty." msgid "Phone can not be empty."
msgstr "No podéis dejar el teléfono en blanco." msgstr "No podéis dejar el teléfono en blanco."
#: pkg/contacts.go:260 #: pkg/contacts.go:261
msgid "This value is not a valid phone number." msgid "This value is not a valid phone number."
msgstr "Este valor no es un teléfono válido." msgstr "Este valor no es un teléfono válido."
#: pkg/contacts.go:266 #: pkg/contacts.go:267
msgid "This value is not a valid web address. It should be like https://domain.com/." msgid "This value is not a valid web address. It should be like https://domain.com/."
msgstr "Este valor no es una dirección web válida. Tiene que ser parecida a https://dominio.es/." msgstr "Este valor no es una dirección web válida. Tiene que ser parecida a https://dominio.es/."
#: pkg/contacts.go:268 #: pkg/contacts.go:269
msgid "Address can not be empty." msgid "Address can not be empty."
msgstr "No podéis dejar la dirección en blanco." msgstr "No podéis dejar la dirección en blanco."
#: pkg/contacts.go:269 #: pkg/contacts.go:270
msgid "City can not be empty." msgid "City can not be empty."
msgstr "No podéis dejar la población en blanco." msgstr "No podéis dejar la población en blanco."
#: pkg/contacts.go:270 #: pkg/contacts.go:271
msgid "Province can not be empty." msgid "Province can not be empty."
msgstr "No podéis dejar la provincia en blanco." msgstr "No podéis dejar la provincia en blanco."
#: pkg/contacts.go:271 #: pkg/contacts.go:272
msgid "Postal code can not be empty." msgid "Postal code can not be empty."
msgstr "No podéis dejar el código postal en blanco." msgstr "No podéis dejar el código postal en blanco."
#: pkg/contacts.go:272 #: pkg/contacts.go:273
msgid "This value is not a valid postal code." msgid "This value is not a valid postal code."
msgstr "Este valor no es un código postal válido válido." msgstr "Este valor no es un código postal válido válido."
#: pkg/contacts.go:274 #: pkg/contacts.go:275
msgid "Selected country is not valid." msgid "Selected country is not valid."
msgstr "Habéis escogido un país que no es válido." msgstr "Habéis escogido un país que no es válido."

View File

@ -342,11 +342,13 @@ input.width-2x {
color: var(--numerus--color--red); color: var(--numerus--color--red);
} }
[lang="en"] input:not([required]) + label::after { [lang="en"] input:not([required]) + label::after,
[lang="en"] select:not([required]) + label::after {
content: " (optional)" content: " (optional)"
} }
[lang="ca"] input:not([required]) + label::after, [lang="es"] input:not([required]) + label::after { [lang="ca"] input:not([required]) + label::after, [lang="es"] input:not([required]) + label::after,
[lang="ca"] select:not([required]) + label::after, [lang="es"] select:not([required]) + label::after {
content: " (opcional)" content: " (opcional)"
} }

View File

@ -18,7 +18,11 @@
<div class="input {{ if .Errors }}has-errors{{ end }}"> <div class="input {{ if .Errors }}has-errors{{ end }}">
<select id="{{ .Name }}-field" name="{{ .Name }}" <select id="{{ .Name }}-field" name="{{ .Name }}"
{{- range $attribute := .Attributes }} {{$attribute}} {{ end -}} {{- range $attribute := .Attributes }} {{$attribute}} {{ end -}}
{{ if .Required }}required="required"{{ end }}
> >
{{- with .EmptyLabel }}
<option value="">{{ . }}</option>
{{- end}}
{{- range $option := .Options }} {{- range $option := .Options }}
<option value="{{ .Value }}" <option value="{{ .Value }}"
{{- if eq .Value $.Selected }} selected="selected"{{ end }}>{{ .Label }}</option> {{- if eq .Value $.Selected }} selected="selected"{{ end }}>{{ .Label }}</option>