Compare commits

..

No commits in common. "b1c653e7de82477cbae233710a858392ed8d3e76" and "586db8d553a732df731a6cb58ed248c22d727e76" have entirely different histories.

23 changed files with 16434 additions and 681 deletions

View File

@ -83,6 +83,7 @@ type Tax struct {
} }
type TaxDetailsPage struct { type TaxDetailsPage struct {
Title string
BusinessName string BusinessName string
VATIN string VATIN string
TradeName string TradeName string
@ -103,7 +104,9 @@ type TaxDetailsPage struct {
func CompanyTaxDetailsHandler() http.Handler { func CompanyTaxDetailsHandler() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
locale := getLocale(r) locale := getLocale(r)
page := &TaxDetailsPage{} page := &TaxDetailsPage{
Title: pgettext("title", "Tax Details", locale),
}
company := mustGetCompany(r) company := mustGetCompany(r)
conn := getConn(r) conn := getConn(r)
if r.Method == "POST" { if r.Method == "POST" {

View File

@ -12,6 +12,7 @@ type ContactEntry struct {
} }
type ContactsIndexPage struct { type ContactsIndexPage struct {
Title string
Contacts []*ContactEntry Contacts []*ContactEntry
} }
@ -37,7 +38,9 @@ func ContactsHandler() http.Handler {
conn.MustExec(r.Context(), "insert into contact (company_id, business_name, vatin, trade_name, phone, email, web, address, province, city, postal_code, country_code) values ($1, $2, ($12 || $3)::vatin, $4, parse_packed_phone_number($5, $12), $6, $7, $8, $9, $10, $11, $12)", company.Id, page.BusinessName, page.VATIN, page.TradeName, page.Phone, page.Email, page.Web, page.Address, page.City, page.Province, page.PostalCode, page.CountryCode) conn.MustExec(r.Context(), "insert into contact (company_id, business_name, vatin, trade_name, phone, email, web, address, province, city, postal_code, country_code) values ($1, $2, ($12 || $3)::vatin, $4, parse_packed_phone_number($5, $12), $6, $7, $8, $9, $10, $11, $12)", company.Id, page.BusinessName, page.VATIN, page.TradeName, page.Phone, page.Email, page.Web, page.Address, page.City, page.Province, page.PostalCode, page.CountryCode)
http.Redirect(w, r, "/company/"+company.Slug+"/contacts", http.StatusSeeOther) http.Redirect(w, r, "/company/"+company.Slug+"/contacts", http.StatusSeeOther)
} else { } else {
locale := getLocale(r)
page := &ContactsIndexPage{ page := &ContactsIndexPage{
Title: pgettext("title", "Customers", locale),
Contacts: mustGetContactEntries(r.Context(), conn, company), Contacts: mustGetContactEntries(r.Context(), conn, company),
} }
mustRenderAppTemplate(w, r, "contacts-index.gohtml", page) mustRenderAppTemplate(w, r, "contacts-index.gohtml", page)
@ -69,6 +72,7 @@ func mustGetContactEntries(ctx context.Context, conn *Conn, company *Company) []
} }
type NewContactPage struct { type NewContactPage struct {
Title string
BusinessName string BusinessName string
VATIN string VATIN string
TradeName string TradeName string
@ -88,6 +92,7 @@ func NewContactHandler() http.Handler {
locale := getLocale(r) locale := getLocale(r)
conn := getConn(r) conn := getConn(r)
page := &NewContactPage{ page := &NewContactPage{
Title: pgettext("title", "New Contact", locale),
CountryCode: "ES", CountryCode: "ES",
Countries: mustGetCountryOptions(r.Context(), conn, locale), Countries: mustGetCountryOptions(r.Context(), conn, locale),
} }

View File

@ -1,73 +0,0 @@
package pkg
import (
"context"
"net/mail"
)
type InputField struct {
Name string
Label string
Type string
Value string
Required bool
Errors []error
}
func (field *InputField) Equals(other *InputField) bool {
return field.Value == other.Value
}
func (field *InputField) IsEmpty() bool {
return field.Value == ""
}
func (field *InputField) HasValidEmail() bool {
_, err := mail.ParseAddress(field.Value)
return err == nil
}
type SelectOption struct {
Value string
Label string
}
type SelectField struct {
Name string
Label string
Selected string
Options []*SelectOption
Errors []error
}
func (field *SelectField) HasValidOption() bool {
for _, option := range field.Options {
if option.Value == field.Selected {
return true
}
}
return false
}
func MustGetOptions(ctx context.Context, conn *Conn, sql string, args ...interface{}) []*SelectOption {
rows, err := conn.Query(ctx, sql, args...)
if err != nil {
panic(err)
}
defer rows.Close()
var options []*SelectOption
for rows.Next() {
option := &SelectOption{}
err = rows.Scan(&option.Value, &option.Label)
if err != nil {
panic(err)
}
options = append(options, option)
}
if rows.Err() != nil {
panic(rows.Err())
}
return options
}

View File

@ -66,10 +66,6 @@ func pgettext(context string, str string, locale *Locale) string {
return locale.GetC(str, context) return locale.GetC(str, context)
} }
func gettext(str string, locale *Locale) string {
return locale.Get(str)
}
func mustGetAvailableLanguages(db *Db) []language.Tag { func mustGetAvailableLanguages(db *Db) []language.Tag {
rows, err := db.Query(context.Background(), "select lang_tag from language where selectable") rows, err := db.Query(context.Background(), "select lang_tag from language where selectable")
if err != nil { if err != nil {

View File

@ -64,7 +64,6 @@ func LogoutHandler() http.Handler {
conn := getConn(r) conn := getConn(r)
conn.MustExec(r.Context(), "select logout()") conn.MustExec(r.Context(), "select logout()")
http.SetCookie(w, createSessionCookie("", -24*time.Hour)) http.SetCookie(w, createSessionCookie("", -24*time.Hour))
http.Redirect(w, r, "/login", http.StatusSeeOther)
}) })
} }

View File

@ -2,9 +2,7 @@ package pkg
import ( import (
"context" "context"
"errors"
"net/http" "net/http"
"strings"
) )
type LanguageOption struct { type LanguageOption struct {
@ -12,88 +10,14 @@ type LanguageOption struct {
Name string Name string
} }
type profileForm struct { type ProfilePage struct {
Name *InputField Title string
Email *InputField Name string
Password *InputField Email string
PasswordConfirm *InputField Password string
Language *SelectField PasswordConfirm string
Valid bool Language string
} Languages []LanguageOption
func newProfileForm(ctx context.Context, conn *Conn, locale *Locale) *profileForm {
automaticOption := pgettext("language option", "Automatic", locale)
languages := MustGetOptions(ctx, conn, "select 'und', $1 union all select lang_tag, endonym from language where selectable", automaticOption)
return &profileForm{
Name: &InputField{
Name: "name",
Label: pgettext("input", "User name", locale),
Type: "text",
Required: true,
},
Email: &InputField{
Name: "email",
Label: pgettext("input", "Email", locale),
Type: "email",
Required: true,
},
Password: &InputField{
Name: "password",
Label: pgettext("input", "Password", locale),
Type: "password",
},
PasswordConfirm: &InputField{
Name: "password_confirm",
Label: pgettext("input", "Password Confirmation", locale),
Type: "password",
},
Language: &SelectField{
Name: "language",
Label: pgettext("input", "Language", locale),
Options: languages,
},
}
}
func (form *profileForm) Parse(r *http.Request) error {
if err := r.ParseForm(); err != nil {
return err
}
form.Email.Value = strings.TrimSpace(r.FormValue("email"))
form.Name.Value = strings.TrimSpace(r.FormValue("name"))
form.Password.Value = strings.TrimSpace(r.FormValue("password"))
form.PasswordConfirm.Value = strings.TrimSpace(r.FormValue("password_confirm"))
form.Language.Selected = r.FormValue("language")
return nil
}
func (form *profileForm) Validate(locale *Locale) bool {
form.Valid = true
if form.Email.IsEmpty() {
form.AppendInputError(form.Email, errors.New(gettext("Email can not be empty.", locale)))
} else if !form.Email.HasValidEmail() {
form.AppendInputError(form.Email, errors.New(gettext("This value is not a valid email. It should be like name@domain.com.", locale)))
}
if form.Name.IsEmpty() {
form.AppendInputError(form.Name, errors.New(gettext("Name can not be empty.", locale)))
}
if !form.PasswordConfirm.Equals(form.Password) {
form.AppendInputError(form.PasswordConfirm, errors.New(gettext("Confirmation does not match password.", locale)))
}
if !form.Language.HasValidOption() {
form.AppendSelectError(form.Language, errors.New(gettext("Selected language is not valid.", locale)))
}
return form.Valid
}
func (form *profileForm) AppendInputError(field *InputField, err error) {
field.Errors = append(field.Errors, err)
form.Valid = false
}
func (form *profileForm) AppendSelectError(field *SelectField, err error) {
field.Errors = append(field.Errors, err)
form.Valid = false
} }
func ProfileHandler() http.Handler { func ProfileHandler() http.Handler {
@ -101,29 +25,54 @@ func ProfileHandler() http.Handler {
user := getUser(r) user := getUser(r)
conn := getConn(r) conn := getConn(r)
locale := getLocale(r) locale := getLocale(r)
form := newProfileForm(r.Context(), conn, locale) page := ProfilePage{
if r.Method == "POST" { Title: pgettext("title", "User Settings", locale),
if err := form.Parse(r); err != nil { Email: user.Email,
http.Error(w, err.Error(), http.StatusBadRequest) Language: user.Language.String(),
return
}
if ok := form.Validate(locale); ok {
//goland:noinspection SqlWithoutWhere
cookie := conn.MustGetText(r.Context(), "", "update user_profile set name = $1, email = $2, lang_tag = $3 returning build_cookie()", form.Name.Value, form.Email.Value, form.Language.Selected)
setSessionCookie(w, cookie)
if form.Password.Value != "" {
conn.MustExec(r.Context(), "select change_password($1)", form.Password.Value)
}
company := getCompany(r)
http.Redirect(w, r, "/company/"+company.Slug+"/profile", http.StatusSeeOther)
return
}
w.WriteHeader(http.StatusUnprocessableEntity)
} else {
form.Name.Value = conn.MustGetText(r.Context(), "", "select name from user_profile")
form.Email.Value = user.Email
form.Language.Selected = user.Language.String()
} }
mustRenderAppTemplate(w, r, "profile.gohtml", form) if r.Method == "POST" {
r.ParseForm()
page.Email = r.FormValue("email")
page.Name = r.FormValue("name")
page.Password = r.FormValue("password")
page.PasswordConfirm = r.FormValue("password_confirm")
page.Language = r.FormValue("language")
cookie := conn.MustGetText(r.Context(), "", "update user_profile set name = $1, email = $2, lang_tag = $3 returning build_cookie()", page.Name, page.Email, page.Language)
setSessionCookie(w, cookie)
if page.Password != "" && page.Password == page.PasswordConfirm {
conn.MustExec(r.Context(), "select change_password($1)", page.Password)
}
http.Redirect(w, r, "/profile", http.StatusSeeOther)
return
} else {
page.Languages = mustGetLanguageOptions(r.Context(), conn)
if err := conn.QueryRow(r.Context(), "select name from user_profile").Scan(&page.Name); err != nil {
panic(nil)
}
}
mustRenderAppTemplate(w, r, "profile.gohtml", page)
}) })
} }
func mustGetLanguageOptions(ctx context.Context, conn *Conn) []LanguageOption {
rows, err := conn.Query(ctx, "select lang_tag, endonym from language where selectable")
if err != nil {
panic(err)
}
defer rows.Close()
var langs []LanguageOption
for rows.Next() {
var lang LanguageOption
err = rows.Scan(&lang.Tag, &lang.Name)
if err != nil {
panic(err)
}
langs = append(langs, lang)
}
if rows.Err() != nil {
panic(rows.Err())
}
return langs
}

View File

@ -17,9 +17,6 @@ func mustRenderTemplate(wr io.Writer, r *http.Request, layout string, filename s
t.Funcs(template.FuncMap{ t.Funcs(template.FuncMap{
"gettext": locale.Get, "gettext": locale.Get,
"pgettext": locale.GetC, "pgettext": locale.GetC,
"currentLocale": func() string {
return locale.Language.String()
},
"companyURI": func(uri string) string { "companyURI": func(uri string) string {
if company == nil { if company == nil {
return uri return uri
@ -27,7 +24,7 @@ func mustRenderTemplate(wr io.Writer, r *http.Request, layout string, filename s
return "/company/" + company.Slug + uri return "/company/" + company.Slug + uri
}, },
}) })
if _, err := t.ParseFiles(templateFile(filename), templateFile(layout), templateFile("form.gohtml")); err != nil { if _, err := t.ParseFiles(templateFile(filename), templateFile(layout)); err != nil {
panic(err) panic(err)
} }
if err := t.ExecuteTemplate(wr, layout, data); err != nil { if err := t.ExecuteTemplate(wr, layout, data); err != nil {

349
po/ca.po
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-01-31 15:17+0100\n" "POT-Creation-Date: 2023-01-30 16:46+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"
@ -17,10 +17,10 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
#: web/template/dashboard.gohtml:2 #: web/template/web.gohtml:6 web/template/login.gohtml:9
msgctxt "title" msgctxt "title"
msgid "Dashboard" msgid "Login"
msgstr "Tauler" msgstr "Entrada"
#: web/template/app.gohtml:20 #: web/template/app.gohtml:20
msgctxt "menu" msgctxt "menu"
@ -39,233 +39,198 @@ msgstr "Surt"
#: web/template/app.gohtml:42 #: web/template/app.gohtml:42
msgctxt "nav" msgctxt "nav"
msgid "Dashboard"
msgstr "Tauler"
#: web/template/app.gohtml:43
msgctxt "nav"
msgid "Customers" msgid "Customers"
msgstr "Clients" msgstr "Clients"
#: web/template/login.gohtml:2 web/template/login.gohtml:13 #: web/template/login.gohtml:5
msgctxt "title"
msgid "Login"
msgstr "Entrada"
#: web/template/login.gohtml:9
msgid "Invalid user or password" msgid "Invalid user or password"
msgstr "Nom dusuari o contrasenya incorrectes" msgstr "Nom dusuari o contrasenya incorrectes"
#: web/template/login.gohtml:17 web/template/tax-details.gohtml:27 #: web/template/login.gohtml:13 web/template/profile.gohtml:15
#: web/template/contacts-new.gohtml:27 pkg/profile.go:37 #: web/template/tax-details.gohtml:23 web/template/contacts-new.gohtml:23
msgctxt "input" msgctxt "input"
msgid "Email" msgid "Email"
msgstr "Correu-e" msgstr "Correu-e"
#: web/template/login.gohtml:22 pkg/profile.go:43 #: web/template/login.gohtml:18 web/template/profile.gohtml:23
msgctxt "input" msgctxt "input"
msgid "Password" msgid "Password"
msgstr "Contrasenya" msgstr "Contrasenya"
#: web/template/login.gohtml:25 #: web/template/login.gohtml:21
msgctxt "action" msgctxt "action"
msgid "Login" msgid "Login"
msgstr "Entra" msgstr "Entra"
#: web/template/profile.gohtml:2 web/template/profile.gohtml:7 #: web/template/profile.gohtml:3 pkg/profile.go:29
msgctxt "title" msgctxt "title"
msgid "User Settings" msgid "User Settings"
msgstr "Configuració usuari" msgstr "Configuració usuari"
#: web/template/profile.gohtml:10 #: web/template/profile.gohtml:6
msgctxt "title" msgctxt "title"
msgid "User Access Data" msgid "User Access Data"
msgstr "Dades accés usuari" msgstr "Dades accés usuari"
#: web/template/profile.gohtml:16 #: web/template/profile.gohtml:10
msgctxt "title"
msgid "Password Change"
msgstr "Canvi contrasenya"
#: web/template/profile.gohtml:23
msgctxt "title"
msgid "Language"
msgstr "Idioma"
#: web/template/profile.gohtml:27 web/template/tax-details.gohtml:133
msgctxt "action"
msgid "Save changes"
msgstr "Desa canvis"
#: web/template/contacts-index.gohtml:2
msgctxt "title"
msgid "Customers"
msgstr "Clients"
#: web/template/contacts-index.gohtml:6 web/template/contacts-new.gohtml:60
msgctxt "action"
msgid "New contact"
msgstr "Nou contacte"
#: web/template/contacts-index.gohtml:11
msgctxt "contact"
msgid "All"
msgstr "Tots"
#: web/template/contacts-index.gohtml:12
msgctxt "title"
msgid "Customer"
msgstr "Client"
#: web/template/contacts-index.gohtml:13
msgctxt "title"
msgid "Email"
msgstr "Correu-e"
#: web/template/contacts-index.gohtml:14
msgctxt "title"
msgid "Phone"
msgstr "Telèfon"
#: web/template/contacts-index.gohtml:29
msgid "No customers added yet."
msgstr "No hi ha cap client."
#: web/template/tax-details.gohtml:2 web/template/tax-details.gohtml:7
msgctxt "title"
msgid "Tax Details"
msgstr "Configuració fiscal"
#: web/template/tax-details.gohtml:11 web/template/contacts-new.gohtml:11
msgctxt "input"
msgid "Business name"
msgstr "Nom i cognom"
#: web/template/tax-details.gohtml:15 web/template/contacts-new.gohtml:15
msgctxt "input"
msgid "VAT number"
msgstr "DNI / NIF"
#: web/template/tax-details.gohtml:19 web/template/contacts-new.gohtml:19
msgctxt "input"
msgid "Trade name"
msgstr "Nom comercial"
#: web/template/tax-details.gohtml:23 web/template/contacts-new.gohtml:23
msgctxt "input"
msgid "Phone"
msgstr "Telèfon"
#: web/template/tax-details.gohtml:31 web/template/contacts-new.gohtml:31
msgctxt "input"
msgid "Web"
msgstr "Web"
#: web/template/tax-details.gohtml:35 web/template/contacts-new.gohtml:35
msgctxt "input"
msgid "Address"
msgstr "Adreça"
#: web/template/tax-details.gohtml:39 web/template/contacts-new.gohtml:39
msgctxt "input"
msgid "City"
msgstr "Població"
#: web/template/tax-details.gohtml:43 web/template/contacts-new.gohtml:43
msgctxt "input"
msgid "Province"
msgstr "Província"
#: web/template/tax-details.gohtml:47 web/template/contacts-new.gohtml:47
msgctxt "input"
msgid "Postal code"
msgstr "Codi postal"
#: web/template/tax-details.gohtml:56 web/template/contacts-new.gohtml:56
msgctxt "input"
msgid "Country"
msgstr "País"
#: web/template/tax-details.gohtml:60
msgctxt "input"
msgid "Currency"
msgstr "Moneda"
#: web/template/tax-details.gohtml:78
msgctxt "title"
msgid "Tax Name"
msgstr "Nom import"
#: web/template/tax-details.gohtml:79
msgctxt "title"
msgid "Rate (%)"
msgstr "Percentatge"
#: web/template/tax-details.gohtml:100
msgid "No taxes added yet."
msgstr "No hi ha cap impost."
#: web/template/tax-details.gohtml:106
msgctxt "title"
msgid "New Line"
msgstr "Nova línia"
#: web/template/tax-details.gohtml:111
msgctxt "input"
msgid "Tax name"
msgstr "Nom impost"
#: web/template/tax-details.gohtml:118
msgctxt "input"
msgid "Rate (%)"
msgstr "Percentatge"
#: web/template/tax-details.gohtml:125
msgctxt "action"
msgid "Add new tax"
msgstr "Afegeix nou impost"
#: web/template/contacts-new.gohtml:2 web/template/contacts-new.gohtml:7
msgctxt "title"
msgid "New Contact"
msgstr "Nou contacte"
#: pkg/profile.go:26
msgctxt "language option"
msgid "Automatic"
msgstr "Automàtic"
#: pkg/profile.go:31
msgctxt "input" msgctxt "input"
msgid "User name" msgid "User name"
msgstr "Nom dusuari" msgstr "Nom dusuari"
#: pkg/profile.go:48 #: web/template/profile.gohtml:19
msgctxt "title"
msgid "Password Change"
msgstr "Canvi contrasenya"
#: web/template/profile.gohtml:28
msgctxt "input" msgctxt "input"
msgid "Password Confirmation" msgid "Password Confirmation"
msgstr "Confirmació contrasenya" msgstr "Confirmació contrasenya"
#: pkg/profile.go:53 #: web/template/profile.gohtml:33
msgctxt "input" msgctxt "input"
msgid "Language" msgid "Language"
msgstr "Idioma" msgstr "Idioma"
#: pkg/profile.go:74 #: web/template/profile.gohtml:36
msgid "Email can not be empty." msgctxt "language option"
msgstr "No podeu deixar el correu-e en blanc." msgid "Automatic"
msgstr "Automàtic"
#: pkg/profile.go:76 #: web/template/profile.gohtml:42 web/template/tax-details.gohtml:127
msgid "This value is not a valid email. It should be like name@domain.com." msgctxt "action"
msgstr "Aquest valor no és un correu-e vàlid. Hauria de ser similar a nom@domini.cat." msgid "Save changes"
msgstr "Desa canvis"
#: pkg/profile.go:79 #: web/template/contacts-index.gohtml:2 web/template/contacts-new.gohtml:56
msgid "Name can not be empty." msgctxt "action"
msgstr "No podeu deixar el nom en blanc." msgid "New contact"
msgstr "Nou contacte"
#: pkg/profile.go:82 #: web/template/contacts-index.gohtml:7
msgid "Confirmation does not match password." msgctxt "contact"
msgstr "La confirmació no és igual a la contrasenya." msgid "All"
msgstr "Tots"
#: pkg/profile.go:85 #: web/template/contacts-index.gohtml:8
msgid "Selected language is not valid." msgctxt "title"
msgstr "Heu seleccionat un idioma que no és vàlid." msgid "Customer"
msgstr "Client"
#: web/template/contacts-index.gohtml:9
msgctxt "title"
msgid "Email"
msgstr "Correu-e"
#: web/template/contacts-index.gohtml:10
msgctxt "title"
msgid "Phone"
msgstr "Telèfon"
#: web/template/contacts-index.gohtml:25
msgid "No customers added yet."
msgstr "No hi ha cap client."
#: web/template/tax-details.gohtml:3 pkg/company.go:108
msgctxt "title"
msgid "Tax Details"
msgstr "Configuració fiscal"
#: web/template/tax-details.gohtml:7 web/template/contacts-new.gohtml:7
msgctxt "input"
msgid "Business name"
msgstr "Nom i cognom"
#: web/template/tax-details.gohtml:11 web/template/contacts-new.gohtml:11
msgctxt "input"
msgid "VAT number"
msgstr "DNI / NIF"
#: web/template/tax-details.gohtml:15 web/template/contacts-new.gohtml:15
msgctxt "input"
msgid "Trade name"
msgstr "Nom comercial"
#: web/template/tax-details.gohtml:19 web/template/contacts-new.gohtml:19
msgctxt "input"
msgid "Phone"
msgstr "Telèfon"
#: web/template/tax-details.gohtml:27 web/template/contacts-new.gohtml:27
msgctxt "input"
msgid "Web"
msgstr "Web"
#: web/template/tax-details.gohtml:31 web/template/contacts-new.gohtml:31
msgctxt "input"
msgid "Address"
msgstr "Adreça"
#: web/template/tax-details.gohtml:35 web/template/contacts-new.gohtml:35
msgctxt "input"
msgid "City"
msgstr "Població"
#: web/template/tax-details.gohtml:39 web/template/contacts-new.gohtml:39
msgctxt "input"
msgid "Province"
msgstr "Província"
#: web/template/tax-details.gohtml:43 web/template/contacts-new.gohtml:43
msgctxt "input"
msgid "Postal code"
msgstr "Codi postal"
#: web/template/tax-details.gohtml:52 web/template/contacts-new.gohtml:52
msgctxt "input"
msgid "Country"
msgstr "País"
#: web/template/tax-details.gohtml:56
msgctxt "input"
msgid "Currency"
msgstr "Moneda"
#: web/template/tax-details.gohtml:74
msgctxt "title"
msgid "Tax Name"
msgstr "Nom import"
#: web/template/tax-details.gohtml:75
msgctxt "title"
msgid "Rate (%)"
msgstr "Percentatge"
#: web/template/tax-details.gohtml:96
msgid "No taxes added yet."
msgstr "No hi ha cap impost."
#: web/template/tax-details.gohtml:102
msgctxt "title"
msgid "New Line"
msgstr "Nova línia"
#: web/template/tax-details.gohtml:106
msgctxt "input"
msgid "Tax name"
msgstr "Nom impost"
#: web/template/tax-details.gohtml:112
msgctxt "input"
msgid "Rate (%)"
msgstr "Percentatge"
#: web/template/tax-details.gohtml:119
msgctxt "action"
msgid "Add new tax"
msgstr "Afegeix nou impost"
#: web/template/contacts-new.gohtml:3 pkg/contacts.go:95
msgctxt "title"
msgid "New Contact"
msgstr "Nou contacte"
#: pkg/contacts.go:43
msgctxt "title"
msgid "Customers"
msgstr "Clients"

349
po/es.po
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-01-31 15:17+0100\n" "POT-Creation-Date: 2023-01-30 16:46+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"
@ -17,10 +17,10 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: web/template/dashboard.gohtml:2 #: web/template/web.gohtml:6 web/template/login.gohtml:9
msgctxt "title" msgctxt "title"
msgid "Dashboard" msgid "Login"
msgstr "Panel" msgstr "Entrada"
#: web/template/app.gohtml:20 #: web/template/app.gohtml:20
msgctxt "menu" msgctxt "menu"
@ -39,233 +39,198 @@ msgstr "Salir"
#: web/template/app.gohtml:42 #: web/template/app.gohtml:42
msgctxt "nav" msgctxt "nav"
msgid "Dashboard"
msgstr "Panel"
#: web/template/app.gohtml:43
msgctxt "nav"
msgid "Customers" msgid "Customers"
msgstr "Clientes" msgstr "Clientes"
#: web/template/login.gohtml:2 web/template/login.gohtml:13 #: web/template/login.gohtml:5
msgctxt "title"
msgid "Login"
msgstr "Entrada"
#: web/template/login.gohtml:9
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"
#: web/template/login.gohtml:17 web/template/tax-details.gohtml:27 #: web/template/login.gohtml:13 web/template/profile.gohtml:15
#: web/template/contacts-new.gohtml:27 pkg/profile.go:37 #: web/template/tax-details.gohtml:23 web/template/contacts-new.gohtml:23
msgctxt "input" msgctxt "input"
msgid "Email" msgid "Email"
msgstr "Correo-e" msgstr "Correo-e"
#: web/template/login.gohtml:22 pkg/profile.go:43 #: web/template/login.gohtml:18 web/template/profile.gohtml:23
msgctxt "input" msgctxt "input"
msgid "Password" msgid "Password"
msgstr "Contraseña" msgstr "Contraseña"
#: web/template/login.gohtml:25 #: web/template/login.gohtml:21
msgctxt "action" msgctxt "action"
msgid "Login" msgid "Login"
msgstr "Entrar" msgstr "Entrar"
#: web/template/profile.gohtml:2 web/template/profile.gohtml:7 #: web/template/profile.gohtml:3 pkg/profile.go:29
msgctxt "title" msgctxt "title"
msgid "User Settings" msgid "User Settings"
msgstr "Configuración usuario" msgstr "Configuración usuario"
#: web/template/profile.gohtml:10 #: web/template/profile.gohtml:6
msgctxt "title" msgctxt "title"
msgid "User Access Data" msgid "User Access Data"
msgstr "Datos acceso usuario" msgstr "Datos acceso usuario"
#: web/template/profile.gohtml:16 #: web/template/profile.gohtml:10
msgctxt "title"
msgid "Password Change"
msgstr "Cambio de contraseña"
#: web/template/profile.gohtml:23
msgctxt "title"
msgid "Language"
msgstr "Idioma"
#: web/template/profile.gohtml:27 web/template/tax-details.gohtml:133
msgctxt "action"
msgid "Save changes"
msgstr "Guardar cambios"
#: web/template/contacts-index.gohtml:2
msgctxt "title"
msgid "Customers"
msgstr "Clientes"
#: web/template/contacts-index.gohtml:6 web/template/contacts-new.gohtml:60
msgctxt "action"
msgid "New contact"
msgstr "Nuevo contacto"
#: web/template/contacts-index.gohtml:11
msgctxt "contact"
msgid "All"
msgstr "Todos"
#: web/template/contacts-index.gohtml:12
msgctxt "title"
msgid "Customer"
msgstr "Cliente"
#: web/template/contacts-index.gohtml:13
msgctxt "title"
msgid "Email"
msgstr "Correo-e"
#: web/template/contacts-index.gohtml:14
msgctxt "title"
msgid "Phone"
msgstr "Teléfono"
#: web/template/contacts-index.gohtml:29
msgid "No customers added yet."
msgstr "No hay clientes."
#: web/template/tax-details.gohtml:2 web/template/tax-details.gohtml:7
msgctxt "title"
msgid "Tax Details"
msgstr "Configuración fiscal"
#: web/template/tax-details.gohtml:11 web/template/contacts-new.gohtml:11
msgctxt "input"
msgid "Business name"
msgstr "Nombre y apellidos"
#: web/template/tax-details.gohtml:15 web/template/contacts-new.gohtml:15
msgctxt "input"
msgid "VAT number"
msgstr "DNI / NIF"
#: web/template/tax-details.gohtml:19 web/template/contacts-new.gohtml:19
msgctxt "input"
msgid "Trade name"
msgstr "Nombre comercial"
#: web/template/tax-details.gohtml:23 web/template/contacts-new.gohtml:23
msgctxt "input"
msgid "Phone"
msgstr "Teléfono"
#: web/template/tax-details.gohtml:31 web/template/contacts-new.gohtml:31
msgctxt "input"
msgid "Web"
msgstr "Web"
#: web/template/tax-details.gohtml:35 web/template/contacts-new.gohtml:35
msgctxt "input"
msgid "Address"
msgstr "Dirección"
#: web/template/tax-details.gohtml:39 web/template/contacts-new.gohtml:39
msgctxt "input"
msgid "City"
msgstr "Población"
#: web/template/tax-details.gohtml:43 web/template/contacts-new.gohtml:43
msgctxt "input"
msgid "Province"
msgstr "Provincia"
#: web/template/tax-details.gohtml:47 web/template/contacts-new.gohtml:47
msgctxt "input"
msgid "Postal code"
msgstr "Código postal"
#: web/template/tax-details.gohtml:56 web/template/contacts-new.gohtml:56
msgctxt "input"
msgid "Country"
msgstr "País"
#: web/template/tax-details.gohtml:60
msgctxt "input"
msgid "Currency"
msgstr "Moneda"
#: web/template/tax-details.gohtml:78
msgctxt "title"
msgid "Tax Name"
msgstr "Nombre impuesto"
#: web/template/tax-details.gohtml:79
msgctxt "title"
msgid "Rate (%)"
msgstr "Porcentage"
#: web/template/tax-details.gohtml:100
msgid "No taxes added yet."
msgstr "No hay impuestos."
#: web/template/tax-details.gohtml:106
msgctxt "title"
msgid "New Line"
msgstr "Nueva línea"
#: web/template/tax-details.gohtml:111
msgctxt "input"
msgid "Tax name"
msgstr "Nombre impuesto"
#: web/template/tax-details.gohtml:118
msgctxt "input"
msgid "Rate (%)"
msgstr "Porcentage"
#: web/template/tax-details.gohtml:125
msgctxt "action"
msgid "Add new tax"
msgstr "Añadir nuevo impuesto"
#: web/template/contacts-new.gohtml:2 web/template/contacts-new.gohtml:7
msgctxt "title"
msgid "New Contact"
msgstr "Nuevo contacto"
#: pkg/profile.go:26
msgctxt "language option"
msgid "Automatic"
msgstr "Automático"
#: pkg/profile.go:31
msgctxt "input" msgctxt "input"
msgid "User name" msgid "User name"
msgstr "Nombre de usuario" msgstr "Nombre de usuario"
#: pkg/profile.go:48 #: web/template/profile.gohtml:19
msgctxt "title"
msgid "Password Change"
msgstr "Cambio de contraseña"
#: web/template/profile.gohtml:28
msgctxt "input" msgctxt "input"
msgid "Password Confirmation" msgid "Password Confirmation"
msgstr "Confirmación contrasenya" msgstr "Confirmación contrasenya"
#: pkg/profile.go:53 #: web/template/profile.gohtml:33
msgctxt "input" msgctxt "input"
msgid "Language" msgid "Language"
msgstr "Idioma" msgstr "Idioma"
#: pkg/profile.go:74 #: web/template/profile.gohtml:36
msgid "Email can not be empty." msgctxt "language option"
msgstr "No podéis dejar el correo-e en blanco." msgid "Automatic"
msgstr "Automático"
#: pkg/profile.go:76 #: web/template/profile.gohtml:42 web/template/tax-details.gohtml:127
msgid "This value is not a valid email. It should be like name@domain.com." msgctxt "action"
msgstr "Este valor no es un correo-e válido. Tiene que ser parecido a nombre@dominio.es." msgid "Save changes"
msgstr "Guardar cambios"
#: pkg/profile.go:79 #: web/template/contacts-index.gohtml:2 web/template/contacts-new.gohtml:56
msgid "Name can not be empty." msgctxt "action"
msgstr "No podéis dejar el nombre en blanco." msgid "New contact"
msgstr "Nuevo contacto"
#: pkg/profile.go:82 #: web/template/contacts-index.gohtml:7
msgid "Confirmation does not match password." msgctxt "contact"
msgstr "La confirmación no corresponde con la contraseña." msgid "All"
msgstr "Todos"
#: pkg/profile.go:85 #: web/template/contacts-index.gohtml:8
msgid "Selected language is not valid." msgctxt "title"
msgstr "Habéis escogido un idioma que no es válido." msgid "Customer"
msgstr "Cliente"
#: web/template/contacts-index.gohtml:9
msgctxt "title"
msgid "Email"
msgstr "Correo-e"
#: web/template/contacts-index.gohtml:10
msgctxt "title"
msgid "Phone"
msgstr "Teléfono"
#: web/template/contacts-index.gohtml:25
msgid "No customers added yet."
msgstr "No hay clientes."
#: web/template/tax-details.gohtml:3 pkg/company.go:108
msgctxt "title"
msgid "Tax Details"
msgstr "Configuración fiscal"
#: web/template/tax-details.gohtml:7 web/template/contacts-new.gohtml:7
msgctxt "input"
msgid "Business name"
msgstr "Nombre y apellidos"
#: web/template/tax-details.gohtml:11 web/template/contacts-new.gohtml:11
msgctxt "input"
msgid "VAT number"
msgstr "DNI / NIF"
#: web/template/tax-details.gohtml:15 web/template/contacts-new.gohtml:15
msgctxt "input"
msgid "Trade name"
msgstr "Nombre comercial"
#: web/template/tax-details.gohtml:19 web/template/contacts-new.gohtml:19
msgctxt "input"
msgid "Phone"
msgstr "Teléfono"
#: web/template/tax-details.gohtml:27 web/template/contacts-new.gohtml:27
msgctxt "input"
msgid "Web"
msgstr "Web"
#: web/template/tax-details.gohtml:31 web/template/contacts-new.gohtml:31
msgctxt "input"
msgid "Address"
msgstr "Dirección"
#: web/template/tax-details.gohtml:35 web/template/contacts-new.gohtml:35
msgctxt "input"
msgid "City"
msgstr "Población"
#: web/template/tax-details.gohtml:39 web/template/contacts-new.gohtml:39
msgctxt "input"
msgid "Province"
msgstr "Provincia"
#: web/template/tax-details.gohtml:43 web/template/contacts-new.gohtml:43
msgctxt "input"
msgid "Postal code"
msgstr "Código postal"
#: web/template/tax-details.gohtml:52 web/template/contacts-new.gohtml:52
msgctxt "input"
msgid "Country"
msgstr "País"
#: web/template/tax-details.gohtml:56
msgctxt "input"
msgid "Currency"
msgstr "Moneda"
#: web/template/tax-details.gohtml:74
msgctxt "title"
msgid "Tax Name"
msgstr "Nombre impuesto"
#: web/template/tax-details.gohtml:75
msgctxt "title"
msgid "Rate (%)"
msgstr "Porcentage"
#: web/template/tax-details.gohtml:96
msgid "No taxes added yet."
msgstr "No hay impuestos."
#: web/template/tax-details.gohtml:102
msgctxt "title"
msgid "New Line"
msgstr "Nueva línea"
#: web/template/tax-details.gohtml:106
msgctxt "input"
msgid "Tax name"
msgstr "Nombre impuesto"
#: web/template/tax-details.gohtml:112
msgctxt "input"
msgid "Rate (%)"
msgstr "Porcentage"
#: web/template/tax-details.gohtml:119
msgctxt "action"
msgid "Add new tax"
msgstr "Añadir nuevo impuesto"
#: web/template/contacts-new.gohtml:3 pkg/contacts.go:95
msgctxt "title"
msgid "New Contact"
msgstr "Nuevo contacto"
#: pkg/contacts.go:43
msgctxt "title"
msgid "Customers"
msgstr "Clientes"

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 877 KiB

View File

@ -246,7 +246,7 @@ header {
background-color: var(--numerus--header--background-color); background-color: var(--numerus--header--background-color);
} }
header, nav a { header, nav {
padding: 0 3rem; padding: 0 3rem;
} }
@ -320,19 +320,13 @@ input.width-2x {
top: 1rem; top: 1rem;
} }
.input ul {
font-size: .8em;
padding-left: 1em;
color: var(--numerus--color--red);
}
[lang="en"] input:not([required]) + label::after { [lang="en"] input:not([required]) + label::after {
content: " (optional)" content: " (optional)"
} }
[lang="ca"] input:not([required]) + label::after [lang="ca"] input:not([required]) + label::after
, [lang="es"] input:not([required]) + label::after { , [lang="es"] input:not([required]) + label::after {
content: " (opcional)" content: " (optional)"
} }
.input label, .input input:focus ~ label { .input label, .input input:focus ~ label {
@ -392,11 +386,11 @@ fieldset {
/* Profile Menu */ /* Profile Menu */
#profile-menu { #profilemenu {
position: relative; position: relative;
} }
#profile-menu summary { #profilemenu summary {
width: 7rem; width: 7rem;
height: 7rem; height: 7rem;
margin: 1rem 0; margin: 1rem 0;
@ -407,19 +401,19 @@ fieldset {
border: none; border: none;
} }
#profile-menu summary::-webkit-details-marker { #profilemenu summary::-webkit-details-marker {
display: none; display: none;
} }
#profile-menu summary, #profile-menu button { #profilemenu summary, #profilemenu button {
cursor: pointer; cursor: pointer;
} }
#profile-menu summary, #profile-menu ul { #profilemenu summary, #profilemenu ul {
background-color: var(--numerus--background-color); background-color: var(--numerus--background-color);
} }
#profile-menu[open] summary::before { #profilemenu[open] summary::before {
background-color: var(--numerus--header--background-color); background-color: var(--numerus--header--background-color);
position: fixed; position: fixed;
top: 0; top: 0;
@ -432,7 +426,7 @@ fieldset {
mix-blend-mode: multiply; mix-blend-mode: multiply;
} }
#profile-menu ul { #profilemenu ul {
list-style: none; list-style: none;
position: absolute; position: absolute;
right: -1.875em; right: -1.875em;
@ -441,11 +435,11 @@ fieldset {
z-index: 20; z-index: 20;
} }
#profile-menu li + li { #profilemenu li + li {
border-top: 1px solid var(--numerus--color--black); border-top: 1px solid var(--numerus--color--black);
} }
#profile-menu button, #profile-menu a { #profilemenu button, #profilemenu a {
font-size: 2rem; font-size: 2rem;
font-style: italic; font-style: italic;
height: 8rem; height: 8rem;
@ -459,15 +453,15 @@ fieldset {
text-transform: initial; text-transform: initial;
} }
#profile-menu li i[class^='ri-'] { #profilemenu li i[class^='ri-'] {
margin-right: 2rem; margin-right: 2rem;
color: var(--numerus--color--dark-gray); color: var(--numerus--color--dark-gray);
} }
#profile-menu summary:hover #profilemenu summary:hover
, #profile-menu summary:focus , #profilemenu summary:focus
, #profile-menu button:hover , #profilemenu button:hover
, #profile-menu a:hover , #profilemenu a:hover
, nav a:hover , nav a:hover
{ {
background-color: var(--numerus--color--light-gray); background-color: var(--numerus--color--light-gray);
@ -488,7 +482,6 @@ fieldset {
} }
[class^='ri-'], [class*=' ri-'] { [class^='ri-'], [class*=' ri-'] {
/*noinspection CssNoGenericFontName*/
font-family: 'remixicon' !important; font-family: 'remixicon' !important;
font-style: normal; font-style: normal;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;

View File

@ -1,15 +1,15 @@
<!doctype html> <!doctype html>
<html lang="{{ currentLocale }}"> <html lang="en">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ template "title" . }} — Numerus</title> <title>{{ .Title }} — Numerus</title>
<link rel="stylesheet" type="text/css" media="screen" href="/static/numerus.css"> <link rel="stylesheet" type="text/css" media="screen" href="/static/numerus.css">
</head> </head>
<body> <body>
<header> <header>
<h1><img src="/static/numerus.svg" alt="Numerus" width="261" height="33"></h1> <h1><img src="/static/numerus.svg" alt="Numerus" width="261" height="33"></h1>
<details id="profile-menu"> <details id="profilemenu">
<summary> <summary>
<i class="ri-eye-close-line ri-3x"></i> <i class="ri-eye-close-line ri-3x"></i>
</summary> </summary>
@ -39,7 +39,6 @@
</header> </header>
<nav aria-label="{{( pgettext "Main" "title" )}}"> <nav aria-label="{{( pgettext "Main" "title" )}}">
<ul> <ul>
<li><a href="{{ companyURI "/" }}">{{( pgettext "Dashboard" "nav" )}}</a></li>
<li><a href="{{ companyURI "/contacts" }}">{{( pgettext "Customers" "nav" )}}</a></li> <li><a href="{{ companyURI "/contacts" }}">{{( pgettext "Customers" "nav" )}}</a></li>
</ul> </ul>
</nav> </nav>

View File

@ -1,7 +1,3 @@
{{ define "title" -}}
{{( pgettext "Customers" "title" )}}
{{- end }}
{{ define "content" }} {{ define "content" }}
<a class="primary button" href="{{ companyURI "/contacts/new" }}">{{( pgettext "New contact" "action" )}}</a> <a class="primary button" href="{{ companyURI "/contacts/new" }}">{{( pgettext "New contact" "action" )}}</a>

View File

@ -1,7 +1,3 @@
{{ define "title" -}}
{{( pgettext "New Contact" "title" )}}
{{- end }}
{{ define "content" }} {{ define "content" }}
<section class="dialog-content"> <section class="dialog-content">
<h2>{{(pgettext "New Contact" "title")}}</h2> <h2>{{(pgettext "New Contact" "title")}}</h2>

View File

@ -1,6 +1,2 @@
{{ define "title" -}}
{{( pgettext "Dashboard" "title" )}}
{{- end }}
{{ define "content" }} {{ define "content" }}
{{- end }} {{- end }}

View File

@ -1,33 +0,0 @@
{{ define "input-field" -}}
<div class="input {{ if .Errors }}has-errors{{ end }}">
<input type="{{ .Type }}" name="{{ .Name }}" id="{{ .Name }}-field"
{{ if .Required }}required="required"{{ end }} value="{{ .Value }}" placeholder="{{ .Label }}">
<label for="{{ .Name }}-field">{{ .Label }}</label>
{{ if .Errors }}
<ul>
{{- range $error := .Errors }}
<li>{{ . }}</li>
{{- end }}
</ul>
{{ end }}
</div>
{{- end }}
{{ define "select-field" -}}
<div class="input {{ if .Errors }}has-errors{{ end }}">
<select id="{{ .Name }}-field" name="{{ .Name }}">
{{- range $option := .Options }}
<option value="{{ .Value }}"
{{ if eq .Value $.Selected }}selected="selected"{{ end }}>{{ .Label }}</option>
{{- end }}
</select>
<label for="{{ .Name }}-field">{{ .Label }}</label>
{{ if .Errors }}
<ul>
{{- range $error := .Errors }}
<li>{{ . }}</li>
{{- end }}
</ul>
{{ end }}
</div>
{{- end }}

View File

@ -1,7 +1,3 @@
{{ define "title" -}}
{{( pgettext "Login" "title" )}}
{{- end }}
{{ define "content" }} {{ define "content" }}
<h1><img src="/static/numerus.svg" alt="Numerus" width="620" height="77"></h1> <h1><img src="/static/numerus.svg" alt="Numerus" width="620" height="77"></h1>
{{ if .LoginError -}} {{ if .LoginError -}}

View File

@ -1,31 +1,46 @@
{{ define "title" -}}
{{( pgettext "User Settings" "title" )}}
{{- end }}
{{ define "content" }} {{ define "content" }}
<section class="dialog-content"> <section class="dialog-content">
<h2>{{(pgettext "User Settings" "title")}}</h2> <h2>{{(pgettext "User Settings" "title")}}</h2>
<form method="POST"> <form method="POST" action="/profile">
<fieldset class="full-width"> <fieldset class="full-width">
<legend>{{( pgettext "User Access Data" "title" )}}</legend> <legend>{{( pgettext "User Access Data" "title" )}}</legend>
{{ template "input-field" .Name }} <div class="input">
{{ template "input-field" .Email }} <input type="text" name="name" id="name" required="required" value="{{ .Name }}" placeholder="{{( pgettext "User name" "input" )}}">
</fieldset> <label for="name">{{( pgettext "User name" "input" )}}</label>
<fieldset class="full-width"> </div>
<legend>{{( pgettext "Password Change" "title" )}}</legend>
{{ template "input-field" .Password }} <div class="input">
{{ template "input-field" .PasswordConfirm }} <input type="email" name="email" id="email" required="required" value="{{ .Email }}" placeholder="{{( pgettext "Email" "input" )}}">
</fieldset> <label for="email">{{( pgettext "Email" "input" )}}</label>
</div>
</fieldset>
<fieldset class="full-width">
<legend>{{( pgettext "Password Change" "title" )}}</legend>
<fieldset> <div class="input">
<legend id="language-legend">{{( pgettext "Language" "title" )}}</legend> <input type="password" name="password" id="password" value="{{ .Password }}" placeholder="{{( pgettext "Password" "input" )}}">
<label for="password">{{( pgettext "Password" "input" )}}</label>
</div>
{{ template "select-field" .Language }} <div class="input">
<input type="password" name="password_confirm" id="password_confirm" value="{{ .PasswordConfirm }}" placeholder="{{( pgettext "Password Confirmation" "input" )}}">
<label for="password_confirm">{{( pgettext "Password Confirmation" "input" )}}</label>
</div>
</fieldset>
<button type="submit">{{( pgettext "Save changes" "action" )}}</button> <fieldset>
</fieldset> <legend id="language-legend">{{( pgettext "Language" "input" )}}</legend>
</form>
</section> <select id="language" name="language" aria-labelledby="language-legend">
<option value="und">{{( pgettext "Automatic" "language option" )}}</option>
{{- range $language := .Languages }}
<option value="{{ .Tag }}" {{ if eq .Tag $.Language }}selected="selected"{{ end }}>{{ .Name }}</option>
{{- end }}
</select>
<button type="submit">{{( pgettext "Save changes" "action" )}}</button>
</fieldset>
</form>
</section>
{{- end }} {{- end }}

View File

@ -1,7 +1,3 @@
{{ define "title" -}}
{{( pgettext "Tax Details" "title" )}}
{{- end }}
{{ define "content" }} {{ define "content" }}
<section class="dialog-content"> <section class="dialog-content">
<h2>{{(pgettext "Tax Details" "title")}}</h2> <h2>{{(pgettext "Tax Details" "title")}}</h2>
@ -89,7 +85,7 @@
<td>{{ .Rate }}</td> <td>{{ .Rate }}</td>
<td> <td>
<form method="POST" action="{{ companyURI "/tax"}}/{{ .Id }}"> <form method="POST" action="{{ companyURI "/tax"}}/{{ .Id }}">
<input type="hidden" name="_method" value="DELETE"/> <input type="hidden" name="_method" name="DELETE"/>
<button class="icon" aria-label="{{( gettext "Delete tax" )}}" type="submit"><i class="ri-delete-back-2-line"></i></button> <button class="icon" aria-label="{{( gettext "Delete tax" )}}" type="submit"><i class="ri-delete-back-2-line"></i></button>
</form> </form>
</td> </td>

View File

@ -1,9 +1,9 @@
<!doctype html> <!doctype html>
<html lang="{{ currentLocale }}"> <html lang="en">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ template "title" . }} — Numerus</title> <title>{{( pgettext "Login" "title" )}} — Numerus</title>
<link rel="stylesheet" type="text/css" media="screen" href="/static/numerus.css"> <link rel="stylesheet" type="text/css" media="screen" href="/static/numerus.css">
</head> </head>
<body class="web"> <body class="web">