Compare commits
12 Commits
586db8d553
...
b1c653e7de
Author | SHA1 | Date | |
---|---|---|---|
b1c653e7de | |||
e0abf98bb1 | |||
3b3c3bd302 | |||
e56e08a68f | |||
9f17f55547 | |||
89256d5b4c | |||
3117c9a268 | |||
873d36abab | |||
93ec8b74c0 | |||
5fc92a5748 | |||
4d452c5522 | |||
9aee33511a |
@ -83,7 +83,6 @@ type Tax struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type TaxDetailsPage struct {
|
type TaxDetailsPage struct {
|
||||||
Title string
|
|
||||||
BusinessName string
|
BusinessName string
|
||||||
VATIN string
|
VATIN string
|
||||||
TradeName string
|
TradeName string
|
||||||
@ -104,9 +103,7 @@ 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" {
|
||||||
|
@ -12,7 +12,6 @@ type ContactEntry struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ContactsIndexPage struct {
|
type ContactsIndexPage struct {
|
||||||
Title string
|
|
||||||
Contacts []*ContactEntry
|
Contacts []*ContactEntry
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,9 +37,7 @@ 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)
|
||||||
@ -72,7 +69,6 @@ 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
|
||||||
@ -92,7 +88,6 @@ 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),
|
||||||
}
|
}
|
||||||
|
73
pkg/form.go
Normal file
73
pkg/form.go
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
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
|
||||||
|
}
|
@ -66,6 +66,10 @@ 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 {
|
||||||
|
@ -64,6 +64,7 @@ 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)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
157
pkg/profile.go
157
pkg/profile.go
@ -2,7 +2,9 @@ package pkg
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type LanguageOption struct {
|
type LanguageOption struct {
|
||||||
@ -10,14 +12,88 @@ type LanguageOption struct {
|
|||||||
Name string
|
Name string
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProfilePage struct {
|
type profileForm struct {
|
||||||
Title string
|
Name *InputField
|
||||||
Name string
|
Email *InputField
|
||||||
Email string
|
Password *InputField
|
||||||
Password string
|
PasswordConfirm *InputField
|
||||||
PasswordConfirm string
|
Language *SelectField
|
||||||
Language string
|
Valid bool
|
||||||
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 {
|
||||||
@ -25,54 +101,29 @@ func ProfileHandler() http.Handler {
|
|||||||
user := getUser(r)
|
user := getUser(r)
|
||||||
conn := getConn(r)
|
conn := getConn(r)
|
||||||
locale := getLocale(r)
|
locale := getLocale(r)
|
||||||
page := ProfilePage{
|
form := newProfileForm(r.Context(), conn, locale)
|
||||||
Title: pgettext("title", "User Settings", locale),
|
|
||||||
Email: user.Email,
|
|
||||||
Language: user.Language.String(),
|
|
||||||
}
|
|
||||||
if r.Method == "POST" {
|
if r.Method == "POST" {
|
||||||
r.ParseForm()
|
if err := form.Parse(r); err != nil {
|
||||||
page.Email = r.FormValue("email")
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
page.Name = r.FormValue("name")
|
return
|
||||||
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)
|
if ok := form.Validate(locale); ok {
|
||||||
return
|
//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 {
|
} else {
|
||||||
page.Languages = mustGetLanguageOptions(r.Context(), conn)
|
form.Name.Value = conn.MustGetText(r.Context(), "", "select name from user_profile")
|
||||||
if err := conn.QueryRow(r.Context(), "select name from user_profile").Scan(&page.Name); err != nil {
|
form.Email.Value = user.Email
|
||||||
panic(nil)
|
form.Language.Selected = user.Language.String()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
mustRenderAppTemplate(w, r, "profile.gohtml", page)
|
mustRenderAppTemplate(w, r, "profile.gohtml", form)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
@ -17,6 +17,9 @@ 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
|
||||||
@ -24,7 +27,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)); err != nil {
|
if _, err := t.ParseFiles(templateFile(filename), templateFile(layout), templateFile("form.gohtml")); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
if err := t.ExecuteTemplate(wr, layout, data); err != nil {
|
if err := t.ExecuteTemplate(wr, layout, data); err != nil {
|
||||||
|
155
po/ca.po
155
po/ca.po
@ -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-30 16:46+0100\n"
|
"POT-Creation-Date: 2023-01-31 15:17+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/web.gohtml:6 web/template/login.gohtml:9
|
#: web/template/dashboard.gohtml:2
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "Login"
|
msgid "Dashboard"
|
||||||
msgstr "Entrada"
|
msgstr "Tauler"
|
||||||
|
|
||||||
#: web/template/app.gohtml:20
|
#: web/template/app.gohtml:20
|
||||||
msgctxt "menu"
|
msgctxt "menu"
|
||||||
@ -39,198 +39,233 @@ 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:5
|
#: web/template/login.gohtml:2 web/template/login.gohtml:13
|
||||||
|
msgctxt "title"
|
||||||
|
msgid "Login"
|
||||||
|
msgstr "Entrada"
|
||||||
|
|
||||||
|
#: web/template/login.gohtml:9
|
||||||
msgid "Invalid user or password"
|
msgid "Invalid user or password"
|
||||||
msgstr "Nom d’usuari o contrasenya incorrectes"
|
msgstr "Nom d’usuari o contrasenya incorrectes"
|
||||||
|
|
||||||
#: web/template/login.gohtml:13 web/template/profile.gohtml:15
|
#: web/template/login.gohtml:17 web/template/tax-details.gohtml:27
|
||||||
#: web/template/tax-details.gohtml:23 web/template/contacts-new.gohtml:23
|
#: web/template/contacts-new.gohtml:27 pkg/profile.go:37
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Email"
|
msgid "Email"
|
||||||
msgstr "Correu-e"
|
msgstr "Correu-e"
|
||||||
|
|
||||||
#: web/template/login.gohtml:18 web/template/profile.gohtml:23
|
#: web/template/login.gohtml:22 pkg/profile.go:43
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Password"
|
msgid "Password"
|
||||||
msgstr "Contrasenya"
|
msgstr "Contrasenya"
|
||||||
|
|
||||||
#: web/template/login.gohtml:21
|
#: web/template/login.gohtml:25
|
||||||
msgctxt "action"
|
msgctxt "action"
|
||||||
msgid "Login"
|
msgid "Login"
|
||||||
msgstr "Entra"
|
msgstr "Entra"
|
||||||
|
|
||||||
#: web/template/profile.gohtml:3 pkg/profile.go:29
|
#: web/template/profile.gohtml:2 web/template/profile.gohtml:7
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "User Settings"
|
msgid "User Settings"
|
||||||
msgstr "Configuració usuari"
|
msgstr "Configuració usuari"
|
||||||
|
|
||||||
#: web/template/profile.gohtml:6
|
#: web/template/profile.gohtml:10
|
||||||
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:10
|
#: web/template/profile.gohtml:16
|
||||||
msgctxt "input"
|
|
||||||
msgid "User name"
|
|
||||||
msgstr "Nom d’usuari"
|
|
||||||
|
|
||||||
#: web/template/profile.gohtml:19
|
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "Password Change"
|
msgid "Password Change"
|
||||||
msgstr "Canvi contrasenya"
|
msgstr "Canvi contrasenya"
|
||||||
|
|
||||||
#: web/template/profile.gohtml:28
|
#: web/template/profile.gohtml:23
|
||||||
msgctxt "input"
|
msgctxt "title"
|
||||||
msgid "Password Confirmation"
|
|
||||||
msgstr "Confirmació contrasenya"
|
|
||||||
|
|
||||||
#: web/template/profile.gohtml:33
|
|
||||||
msgctxt "input"
|
|
||||||
msgid "Language"
|
msgid "Language"
|
||||||
msgstr "Idioma"
|
msgstr "Idioma"
|
||||||
|
|
||||||
#: web/template/profile.gohtml:36
|
#: web/template/profile.gohtml:27 web/template/tax-details.gohtml:133
|
||||||
msgctxt "language option"
|
|
||||||
msgid "Automatic"
|
|
||||||
msgstr "Automàtic"
|
|
||||||
|
|
||||||
#: web/template/profile.gohtml:42 web/template/tax-details.gohtml:127
|
|
||||||
msgctxt "action"
|
msgctxt "action"
|
||||||
msgid "Save changes"
|
msgid "Save changes"
|
||||||
msgstr "Desa canvis"
|
msgstr "Desa canvis"
|
||||||
|
|
||||||
#: web/template/contacts-index.gohtml:2 web/template/contacts-new.gohtml:56
|
#: 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"
|
msgctxt "action"
|
||||||
msgid "New contact"
|
msgid "New contact"
|
||||||
msgstr "Nou contacte"
|
msgstr "Nou contacte"
|
||||||
|
|
||||||
#: web/template/contacts-index.gohtml:7
|
#: web/template/contacts-index.gohtml:11
|
||||||
msgctxt "contact"
|
msgctxt "contact"
|
||||||
msgid "All"
|
msgid "All"
|
||||||
msgstr "Tots"
|
msgstr "Tots"
|
||||||
|
|
||||||
#: web/template/contacts-index.gohtml:8
|
#: web/template/contacts-index.gohtml:12
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "Customer"
|
msgid "Customer"
|
||||||
msgstr "Client"
|
msgstr "Client"
|
||||||
|
|
||||||
#: web/template/contacts-index.gohtml:9
|
#: web/template/contacts-index.gohtml:13
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "Email"
|
msgid "Email"
|
||||||
msgstr "Correu-e"
|
msgstr "Correu-e"
|
||||||
|
|
||||||
#: web/template/contacts-index.gohtml:10
|
#: web/template/contacts-index.gohtml:14
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "Phone"
|
msgid "Phone"
|
||||||
msgstr "Telèfon"
|
msgstr "Telèfon"
|
||||||
|
|
||||||
#: web/template/contacts-index.gohtml:25
|
#: web/template/contacts-index.gohtml:29
|
||||||
msgid "No customers added yet."
|
msgid "No customers added yet."
|
||||||
msgstr "No hi ha cap client."
|
msgstr "No hi ha cap client."
|
||||||
|
|
||||||
#: web/template/tax-details.gohtml:3 pkg/company.go:108
|
#: web/template/tax-details.gohtml:2 web/template/tax-details.gohtml:7
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "Tax Details"
|
msgid "Tax Details"
|
||||||
msgstr "Configuració fiscal"
|
msgstr "Configuració fiscal"
|
||||||
|
|
||||||
#: web/template/tax-details.gohtml:7 web/template/contacts-new.gohtml:7
|
#: web/template/tax-details.gohtml:11 web/template/contacts-new.gohtml:11
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Business name"
|
msgid "Business name"
|
||||||
msgstr "Nom i cognom"
|
msgstr "Nom i cognom"
|
||||||
|
|
||||||
#: web/template/tax-details.gohtml:11 web/template/contacts-new.gohtml:11
|
#: web/template/tax-details.gohtml:15 web/template/contacts-new.gohtml:15
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "VAT number"
|
msgid "VAT number"
|
||||||
msgstr "DNI / NIF"
|
msgstr "DNI / NIF"
|
||||||
|
|
||||||
#: web/template/tax-details.gohtml:15 web/template/contacts-new.gohtml:15
|
#: web/template/tax-details.gohtml:19 web/template/contacts-new.gohtml:19
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Trade name"
|
msgid "Trade name"
|
||||||
msgstr "Nom comercial"
|
msgstr "Nom comercial"
|
||||||
|
|
||||||
#: web/template/tax-details.gohtml:19 web/template/contacts-new.gohtml:19
|
#: web/template/tax-details.gohtml:23 web/template/contacts-new.gohtml:23
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Phone"
|
msgid "Phone"
|
||||||
msgstr "Telèfon"
|
msgstr "Telèfon"
|
||||||
|
|
||||||
#: web/template/tax-details.gohtml:27 web/template/contacts-new.gohtml:27
|
#: web/template/tax-details.gohtml:31 web/template/contacts-new.gohtml:31
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Web"
|
msgid "Web"
|
||||||
msgstr "Web"
|
msgstr "Web"
|
||||||
|
|
||||||
#: web/template/tax-details.gohtml:31 web/template/contacts-new.gohtml:31
|
#: web/template/tax-details.gohtml:35 web/template/contacts-new.gohtml:35
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Address"
|
msgid "Address"
|
||||||
msgstr "Adreça"
|
msgstr "Adreça"
|
||||||
|
|
||||||
#: web/template/tax-details.gohtml:35 web/template/contacts-new.gohtml:35
|
#: web/template/tax-details.gohtml:39 web/template/contacts-new.gohtml:39
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "City"
|
msgid "City"
|
||||||
msgstr "Població"
|
msgstr "Població"
|
||||||
|
|
||||||
#: web/template/tax-details.gohtml:39 web/template/contacts-new.gohtml:39
|
#: web/template/tax-details.gohtml:43 web/template/contacts-new.gohtml:43
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Province"
|
msgid "Province"
|
||||||
msgstr "Província"
|
msgstr "Província"
|
||||||
|
|
||||||
#: web/template/tax-details.gohtml:43 web/template/contacts-new.gohtml:43
|
#: web/template/tax-details.gohtml:47 web/template/contacts-new.gohtml:47
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Postal code"
|
msgid "Postal code"
|
||||||
msgstr "Codi postal"
|
msgstr "Codi postal"
|
||||||
|
|
||||||
#: web/template/tax-details.gohtml:52 web/template/contacts-new.gohtml:52
|
#: web/template/tax-details.gohtml:56 web/template/contacts-new.gohtml:56
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Country"
|
msgid "Country"
|
||||||
msgstr "País"
|
msgstr "País"
|
||||||
|
|
||||||
#: web/template/tax-details.gohtml:56
|
#: web/template/tax-details.gohtml:60
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Currency"
|
msgid "Currency"
|
||||||
msgstr "Moneda"
|
msgstr "Moneda"
|
||||||
|
|
||||||
#: web/template/tax-details.gohtml:74
|
#: web/template/tax-details.gohtml:78
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "Tax Name"
|
msgid "Tax Name"
|
||||||
msgstr "Nom import"
|
msgstr "Nom import"
|
||||||
|
|
||||||
#: web/template/tax-details.gohtml:75
|
#: web/template/tax-details.gohtml:79
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "Rate (%)"
|
msgid "Rate (%)"
|
||||||
msgstr "Percentatge"
|
msgstr "Percentatge"
|
||||||
|
|
||||||
#: web/template/tax-details.gohtml:96
|
#: web/template/tax-details.gohtml:100
|
||||||
msgid "No taxes added yet."
|
msgid "No taxes added yet."
|
||||||
msgstr "No hi ha cap impost."
|
msgstr "No hi ha cap impost."
|
||||||
|
|
||||||
#: web/template/tax-details.gohtml:102
|
#: web/template/tax-details.gohtml:106
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "New Line"
|
msgid "New Line"
|
||||||
msgstr "Nova línia"
|
msgstr "Nova línia"
|
||||||
|
|
||||||
#: web/template/tax-details.gohtml:106
|
#: web/template/tax-details.gohtml:111
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Tax name"
|
msgid "Tax name"
|
||||||
msgstr "Nom impost"
|
msgstr "Nom impost"
|
||||||
|
|
||||||
#: web/template/tax-details.gohtml:112
|
#: web/template/tax-details.gohtml:118
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Rate (%)"
|
msgid "Rate (%)"
|
||||||
msgstr "Percentatge"
|
msgstr "Percentatge"
|
||||||
|
|
||||||
#: web/template/tax-details.gohtml:119
|
#: web/template/tax-details.gohtml:125
|
||||||
msgctxt "action"
|
msgctxt "action"
|
||||||
msgid "Add new tax"
|
msgid "Add new tax"
|
||||||
msgstr "Afegeix nou impost"
|
msgstr "Afegeix nou impost"
|
||||||
|
|
||||||
#: web/template/contacts-new.gohtml:3 pkg/contacts.go:95
|
#: web/template/contacts-new.gohtml:2 web/template/contacts-new.gohtml:7
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "New Contact"
|
msgid "New Contact"
|
||||||
msgstr "Nou contacte"
|
msgstr "Nou contacte"
|
||||||
|
|
||||||
#: pkg/contacts.go:43
|
#: pkg/profile.go:26
|
||||||
msgctxt "title"
|
msgctxt "language option"
|
||||||
msgid "Customers"
|
msgid "Automatic"
|
||||||
msgstr "Clients"
|
msgstr "Automàtic"
|
||||||
|
|
||||||
|
#: pkg/profile.go:31
|
||||||
|
msgctxt "input"
|
||||||
|
msgid "User name"
|
||||||
|
msgstr "Nom d’usuari"
|
||||||
|
|
||||||
|
#: pkg/profile.go:48
|
||||||
|
msgctxt "input"
|
||||||
|
msgid "Password Confirmation"
|
||||||
|
msgstr "Confirmació contrasenya"
|
||||||
|
|
||||||
|
#: pkg/profile.go:53
|
||||||
|
msgctxt "input"
|
||||||
|
msgid "Language"
|
||||||
|
msgstr "Idioma"
|
||||||
|
|
||||||
|
#: pkg/profile.go:74
|
||||||
|
msgid "Email can not be empty."
|
||||||
|
msgstr "No podeu deixar el correu-e en blanc."
|
||||||
|
|
||||||
|
#: pkg/profile.go:76
|
||||||
|
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."
|
||||||
|
|
||||||
|
#: pkg/profile.go:79
|
||||||
|
msgid "Name can not be empty."
|
||||||
|
msgstr "No podeu deixar el nom en blanc."
|
||||||
|
|
||||||
|
#: pkg/profile.go:82
|
||||||
|
msgid "Confirmation does not match password."
|
||||||
|
msgstr "La confirmació no és igual a la contrasenya."
|
||||||
|
|
||||||
|
#: pkg/profile.go:85
|
||||||
|
msgid "Selected language is not valid."
|
||||||
|
msgstr "Heu seleccionat un idioma que no és vàlid."
|
||||||
|
155
po/es.po
155
po/es.po
@ -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-30 16:46+0100\n"
|
"POT-Creation-Date: 2023-01-31 15:17+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/web.gohtml:6 web/template/login.gohtml:9
|
#: web/template/dashboard.gohtml:2
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "Login"
|
msgid "Dashboard"
|
||||||
msgstr "Entrada"
|
msgstr "Panel"
|
||||||
|
|
||||||
#: web/template/app.gohtml:20
|
#: web/template/app.gohtml:20
|
||||||
msgctxt "menu"
|
msgctxt "menu"
|
||||||
@ -39,198 +39,233 @@ 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:5
|
#: web/template/login.gohtml:2 web/template/login.gohtml:13
|
||||||
|
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:13 web/template/profile.gohtml:15
|
#: web/template/login.gohtml:17 web/template/tax-details.gohtml:27
|
||||||
#: web/template/tax-details.gohtml:23 web/template/contacts-new.gohtml:23
|
#: web/template/contacts-new.gohtml:27 pkg/profile.go:37
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Email"
|
msgid "Email"
|
||||||
msgstr "Correo-e"
|
msgstr "Correo-e"
|
||||||
|
|
||||||
#: web/template/login.gohtml:18 web/template/profile.gohtml:23
|
#: web/template/login.gohtml:22 pkg/profile.go:43
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Password"
|
msgid "Password"
|
||||||
msgstr "Contraseña"
|
msgstr "Contraseña"
|
||||||
|
|
||||||
#: web/template/login.gohtml:21
|
#: web/template/login.gohtml:25
|
||||||
msgctxt "action"
|
msgctxt "action"
|
||||||
msgid "Login"
|
msgid "Login"
|
||||||
msgstr "Entrar"
|
msgstr "Entrar"
|
||||||
|
|
||||||
#: web/template/profile.gohtml:3 pkg/profile.go:29
|
#: web/template/profile.gohtml:2 web/template/profile.gohtml:7
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "User Settings"
|
msgid "User Settings"
|
||||||
msgstr "Configuración usuario"
|
msgstr "Configuración usuario"
|
||||||
|
|
||||||
#: web/template/profile.gohtml:6
|
#: web/template/profile.gohtml:10
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "User Access Data"
|
msgid "User Access Data"
|
||||||
msgstr "Datos acceso usuario"
|
msgstr "Datos acceso usuario"
|
||||||
|
|
||||||
#: web/template/profile.gohtml:10
|
#: web/template/profile.gohtml:16
|
||||||
msgctxt "input"
|
|
||||||
msgid "User name"
|
|
||||||
msgstr "Nombre de usuario"
|
|
||||||
|
|
||||||
#: web/template/profile.gohtml:19
|
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "Password Change"
|
msgid "Password Change"
|
||||||
msgstr "Cambio de contraseña"
|
msgstr "Cambio de contraseña"
|
||||||
|
|
||||||
#: web/template/profile.gohtml:28
|
#: web/template/profile.gohtml:23
|
||||||
msgctxt "input"
|
msgctxt "title"
|
||||||
msgid "Password Confirmation"
|
|
||||||
msgstr "Confirmación contrasenya"
|
|
||||||
|
|
||||||
#: web/template/profile.gohtml:33
|
|
||||||
msgctxt "input"
|
|
||||||
msgid "Language"
|
msgid "Language"
|
||||||
msgstr "Idioma"
|
msgstr "Idioma"
|
||||||
|
|
||||||
#: web/template/profile.gohtml:36
|
#: web/template/profile.gohtml:27 web/template/tax-details.gohtml:133
|
||||||
msgctxt "language option"
|
|
||||||
msgid "Automatic"
|
|
||||||
msgstr "Automático"
|
|
||||||
|
|
||||||
#: web/template/profile.gohtml:42 web/template/tax-details.gohtml:127
|
|
||||||
msgctxt "action"
|
msgctxt "action"
|
||||||
msgid "Save changes"
|
msgid "Save changes"
|
||||||
msgstr "Guardar cambios"
|
msgstr "Guardar cambios"
|
||||||
|
|
||||||
#: web/template/contacts-index.gohtml:2 web/template/contacts-new.gohtml:56
|
#: 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"
|
msgctxt "action"
|
||||||
msgid "New contact"
|
msgid "New contact"
|
||||||
msgstr "Nuevo contacto"
|
msgstr "Nuevo contacto"
|
||||||
|
|
||||||
#: web/template/contacts-index.gohtml:7
|
#: web/template/contacts-index.gohtml:11
|
||||||
msgctxt "contact"
|
msgctxt "contact"
|
||||||
msgid "All"
|
msgid "All"
|
||||||
msgstr "Todos"
|
msgstr "Todos"
|
||||||
|
|
||||||
#: web/template/contacts-index.gohtml:8
|
#: web/template/contacts-index.gohtml:12
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "Customer"
|
msgid "Customer"
|
||||||
msgstr "Cliente"
|
msgstr "Cliente"
|
||||||
|
|
||||||
#: web/template/contacts-index.gohtml:9
|
#: web/template/contacts-index.gohtml:13
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "Email"
|
msgid "Email"
|
||||||
msgstr "Correo-e"
|
msgstr "Correo-e"
|
||||||
|
|
||||||
#: web/template/contacts-index.gohtml:10
|
#: web/template/contacts-index.gohtml:14
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "Phone"
|
msgid "Phone"
|
||||||
msgstr "Teléfono"
|
msgstr "Teléfono"
|
||||||
|
|
||||||
#: web/template/contacts-index.gohtml:25
|
#: web/template/contacts-index.gohtml:29
|
||||||
msgid "No customers added yet."
|
msgid "No customers added yet."
|
||||||
msgstr "No hay clientes."
|
msgstr "No hay clientes."
|
||||||
|
|
||||||
#: web/template/tax-details.gohtml:3 pkg/company.go:108
|
#: web/template/tax-details.gohtml:2 web/template/tax-details.gohtml:7
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "Tax Details"
|
msgid "Tax Details"
|
||||||
msgstr "Configuración fiscal"
|
msgstr "Configuración fiscal"
|
||||||
|
|
||||||
#: web/template/tax-details.gohtml:7 web/template/contacts-new.gohtml:7
|
#: web/template/tax-details.gohtml:11 web/template/contacts-new.gohtml:11
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Business name"
|
msgid "Business name"
|
||||||
msgstr "Nombre y apellidos"
|
msgstr "Nombre y apellidos"
|
||||||
|
|
||||||
#: web/template/tax-details.gohtml:11 web/template/contacts-new.gohtml:11
|
#: web/template/tax-details.gohtml:15 web/template/contacts-new.gohtml:15
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "VAT number"
|
msgid "VAT number"
|
||||||
msgstr "DNI / NIF"
|
msgstr "DNI / NIF"
|
||||||
|
|
||||||
#: web/template/tax-details.gohtml:15 web/template/contacts-new.gohtml:15
|
#: web/template/tax-details.gohtml:19 web/template/contacts-new.gohtml:19
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Trade name"
|
msgid "Trade name"
|
||||||
msgstr "Nombre comercial"
|
msgstr "Nombre comercial"
|
||||||
|
|
||||||
#: web/template/tax-details.gohtml:19 web/template/contacts-new.gohtml:19
|
#: web/template/tax-details.gohtml:23 web/template/contacts-new.gohtml:23
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Phone"
|
msgid "Phone"
|
||||||
msgstr "Teléfono"
|
msgstr "Teléfono"
|
||||||
|
|
||||||
#: web/template/tax-details.gohtml:27 web/template/contacts-new.gohtml:27
|
#: web/template/tax-details.gohtml:31 web/template/contacts-new.gohtml:31
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Web"
|
msgid "Web"
|
||||||
msgstr "Web"
|
msgstr "Web"
|
||||||
|
|
||||||
#: web/template/tax-details.gohtml:31 web/template/contacts-new.gohtml:31
|
#: web/template/tax-details.gohtml:35 web/template/contacts-new.gohtml:35
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Address"
|
msgid "Address"
|
||||||
msgstr "Dirección"
|
msgstr "Dirección"
|
||||||
|
|
||||||
#: web/template/tax-details.gohtml:35 web/template/contacts-new.gohtml:35
|
#: web/template/tax-details.gohtml:39 web/template/contacts-new.gohtml:39
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "City"
|
msgid "City"
|
||||||
msgstr "Población"
|
msgstr "Población"
|
||||||
|
|
||||||
#: web/template/tax-details.gohtml:39 web/template/contacts-new.gohtml:39
|
#: web/template/tax-details.gohtml:43 web/template/contacts-new.gohtml:43
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Province"
|
msgid "Province"
|
||||||
msgstr "Provincia"
|
msgstr "Provincia"
|
||||||
|
|
||||||
#: web/template/tax-details.gohtml:43 web/template/contacts-new.gohtml:43
|
#: web/template/tax-details.gohtml:47 web/template/contacts-new.gohtml:47
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Postal code"
|
msgid "Postal code"
|
||||||
msgstr "Código postal"
|
msgstr "Código postal"
|
||||||
|
|
||||||
#: web/template/tax-details.gohtml:52 web/template/contacts-new.gohtml:52
|
#: web/template/tax-details.gohtml:56 web/template/contacts-new.gohtml:56
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Country"
|
msgid "Country"
|
||||||
msgstr "País"
|
msgstr "País"
|
||||||
|
|
||||||
#: web/template/tax-details.gohtml:56
|
#: web/template/tax-details.gohtml:60
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Currency"
|
msgid "Currency"
|
||||||
msgstr "Moneda"
|
msgstr "Moneda"
|
||||||
|
|
||||||
#: web/template/tax-details.gohtml:74
|
#: web/template/tax-details.gohtml:78
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "Tax Name"
|
msgid "Tax Name"
|
||||||
msgstr "Nombre impuesto"
|
msgstr "Nombre impuesto"
|
||||||
|
|
||||||
#: web/template/tax-details.gohtml:75
|
#: web/template/tax-details.gohtml:79
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "Rate (%)"
|
msgid "Rate (%)"
|
||||||
msgstr "Porcentage"
|
msgstr "Porcentage"
|
||||||
|
|
||||||
#: web/template/tax-details.gohtml:96
|
#: web/template/tax-details.gohtml:100
|
||||||
msgid "No taxes added yet."
|
msgid "No taxes added yet."
|
||||||
msgstr "No hay impuestos."
|
msgstr "No hay impuestos."
|
||||||
|
|
||||||
#: web/template/tax-details.gohtml:102
|
#: web/template/tax-details.gohtml:106
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "New Line"
|
msgid "New Line"
|
||||||
msgstr "Nueva línea"
|
msgstr "Nueva línea"
|
||||||
|
|
||||||
#: web/template/tax-details.gohtml:106
|
#: web/template/tax-details.gohtml:111
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Tax name"
|
msgid "Tax name"
|
||||||
msgstr "Nombre impuesto"
|
msgstr "Nombre impuesto"
|
||||||
|
|
||||||
#: web/template/tax-details.gohtml:112
|
#: web/template/tax-details.gohtml:118
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Rate (%)"
|
msgid "Rate (%)"
|
||||||
msgstr "Porcentage"
|
msgstr "Porcentage"
|
||||||
|
|
||||||
#: web/template/tax-details.gohtml:119
|
#: web/template/tax-details.gohtml:125
|
||||||
msgctxt "action"
|
msgctxt "action"
|
||||||
msgid "Add new tax"
|
msgid "Add new tax"
|
||||||
msgstr "Añadir nuevo impuesto"
|
msgstr "Añadir nuevo impuesto"
|
||||||
|
|
||||||
#: web/template/contacts-new.gohtml:3 pkg/contacts.go:95
|
#: web/template/contacts-new.gohtml:2 web/template/contacts-new.gohtml:7
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "New Contact"
|
msgid "New Contact"
|
||||||
msgstr "Nuevo contacto"
|
msgstr "Nuevo contacto"
|
||||||
|
|
||||||
#: pkg/contacts.go:43
|
#: pkg/profile.go:26
|
||||||
msgctxt "title"
|
msgctxt "language option"
|
||||||
msgid "Customers"
|
msgid "Automatic"
|
||||||
msgstr "Clientes"
|
msgstr "Automático"
|
||||||
|
|
||||||
|
#: pkg/profile.go:31
|
||||||
|
msgctxt "input"
|
||||||
|
msgid "User name"
|
||||||
|
msgstr "Nombre de usuario"
|
||||||
|
|
||||||
|
#: pkg/profile.go:48
|
||||||
|
msgctxt "input"
|
||||||
|
msgid "Password Confirmation"
|
||||||
|
msgstr "Confirmación contrasenya"
|
||||||
|
|
||||||
|
#: pkg/profile.go:53
|
||||||
|
msgctxt "input"
|
||||||
|
msgid "Language"
|
||||||
|
msgstr "Idioma"
|
||||||
|
|
||||||
|
#: pkg/profile.go:74
|
||||||
|
msgid "Email can not be empty."
|
||||||
|
msgstr "No podéis dejar el correo-e en blanco."
|
||||||
|
|
||||||
|
#: pkg/profile.go:76
|
||||||
|
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."
|
||||||
|
|
||||||
|
#: pkg/profile.go:79
|
||||||
|
msgid "Name can not be empty."
|
||||||
|
msgstr "No podéis dejar el nombre en blanco."
|
||||||
|
|
||||||
|
#: pkg/profile.go:82
|
||||||
|
msgid "Confirmation does not match password."
|
||||||
|
msgstr "La confirmación no corresponde con la contraseña."
|
||||||
|
|
||||||
|
#: pkg/profile.go:85
|
||||||
|
msgid "Selected language is not valid."
|
||||||
|
msgstr "Habéis escogido un idioma que no es válido."
|
||||||
|
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
Before Width: | Height: | Size: 877 KiB |
@ -246,7 +246,7 @@ header {
|
|||||||
background-color: var(--numerus--header--background-color);
|
background-color: var(--numerus--header--background-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
header, nav {
|
header, nav a {
|
||||||
padding: 0 3rem;
|
padding: 0 3rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -320,13 +320,19 @@ 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: " (optional)"
|
content: " (opcional)"
|
||||||
}
|
}
|
||||||
|
|
||||||
.input label, .input input:focus ~ label {
|
.input label, .input input:focus ~ label {
|
||||||
@ -386,11 +392,11 @@ fieldset {
|
|||||||
|
|
||||||
/* Profile Menu */
|
/* Profile Menu */
|
||||||
|
|
||||||
#profilemenu {
|
#profile-menu {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
#profilemenu summary {
|
#profile-menu summary {
|
||||||
width: 7rem;
|
width: 7rem;
|
||||||
height: 7rem;
|
height: 7rem;
|
||||||
margin: 1rem 0;
|
margin: 1rem 0;
|
||||||
@ -401,19 +407,19 @@ fieldset {
|
|||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#profilemenu summary::-webkit-details-marker {
|
#profile-menu summary::-webkit-details-marker {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#profilemenu summary, #profilemenu button {
|
#profile-menu summary, #profile-menu button {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
#profilemenu summary, #profilemenu ul {
|
#profile-menu summary, #profile-menu ul {
|
||||||
background-color: var(--numerus--background-color);
|
background-color: var(--numerus--background-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
#profilemenu[open] summary::before {
|
#profile-menu[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;
|
||||||
@ -426,7 +432,7 @@ fieldset {
|
|||||||
mix-blend-mode: multiply;
|
mix-blend-mode: multiply;
|
||||||
}
|
}
|
||||||
|
|
||||||
#profilemenu ul {
|
#profile-menu ul {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: -1.875em;
|
right: -1.875em;
|
||||||
@ -435,11 +441,11 @@ fieldset {
|
|||||||
z-index: 20;
|
z-index: 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
#profilemenu li + li {
|
#profile-menu li + li {
|
||||||
border-top: 1px solid var(--numerus--color--black);
|
border-top: 1px solid var(--numerus--color--black);
|
||||||
}
|
}
|
||||||
|
|
||||||
#profilemenu button, #profilemenu a {
|
#profile-menu button, #profile-menu a {
|
||||||
font-size: 2rem;
|
font-size: 2rem;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
height: 8rem;
|
height: 8rem;
|
||||||
@ -453,15 +459,15 @@ fieldset {
|
|||||||
text-transform: initial;
|
text-transform: initial;
|
||||||
}
|
}
|
||||||
|
|
||||||
#profilemenu li i[class^='ri-'] {
|
#profile-menu li i[class^='ri-'] {
|
||||||
margin-right: 2rem;
|
margin-right: 2rem;
|
||||||
color: var(--numerus--color--dark-gray);
|
color: var(--numerus--color--dark-gray);
|
||||||
}
|
}
|
||||||
|
|
||||||
#profilemenu summary:hover
|
#profile-menu summary:hover
|
||||||
, #profilemenu summary:focus
|
, #profile-menu summary:focus
|
||||||
, #profilemenu button:hover
|
, #profile-menu button:hover
|
||||||
, #profilemenu a:hover
|
, #profile-menu a:hover
|
||||||
, nav a:hover
|
, nav a:hover
|
||||||
{
|
{
|
||||||
background-color: var(--numerus--color--light-gray);
|
background-color: var(--numerus--color--light-gray);
|
||||||
@ -482,6 +488,7 @@ 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;
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="{{ currentLocale }}">
|
||||||
<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>{{ .Title }} — Numerus</title>
|
<title>{{ template "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="profilemenu">
|
<details id="profile-menu">
|
||||||
<summary>
|
<summary>
|
||||||
<i class="ri-eye-close-line ri-3x"></i>
|
<i class="ri-eye-close-line ri-3x"></i>
|
||||||
</summary>
|
</summary>
|
||||||
@ -39,6 +39,7 @@
|
|||||||
</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>
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
|
{{ 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>
|
||||||
|
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
|
{{ 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>
|
||||||
|
@ -1,2 +1,6 @@
|
|||||||
|
{{ define "title" -}}
|
||||||
|
{{( pgettext "Dashboard" "title" )}}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
{{ define "content" }}
|
{{ define "content" }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
33
web/template/form.gohtml
Normal file
33
web/template/form.gohtml
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
{{ 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 }}
|
@ -1,3 +1,7 @@
|
|||||||
|
{{ 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 -}}
|
||||||
|
@ -1,46 +1,31 @@
|
|||||||
{{ define "content" }}
|
{{ define "title" -}}
|
||||||
<section class="dialog-content">
|
{{( pgettext "User Settings" "title" )}}
|
||||||
<h2>{{(pgettext "User Settings" "title")}}</h2>
|
{{- end }}
|
||||||
<form method="POST" action="/profile">
|
|
||||||
<fieldset class="full-width">
|
{{ define "content" }}
|
||||||
<legend>{{( pgettext "User Access Data" "title" )}}</legend>
|
<section class="dialog-content">
|
||||||
|
<h2>{{(pgettext "User Settings" "title")}}</h2>
|
||||||
<div class="input">
|
<form method="POST">
|
||||||
<input type="text" name="name" id="name" required="required" value="{{ .Name }}" placeholder="{{( pgettext "User name" "input" )}}">
|
<fieldset class="full-width">
|
||||||
<label for="name">{{( pgettext "User name" "input" )}}</label>
|
<legend>{{( pgettext "User Access Data" "title" )}}</legend>
|
||||||
</div>
|
|
||||||
|
{{ template "input-field" .Name }}
|
||||||
<div class="input">
|
{{ template "input-field" .Email }}
|
||||||
<input type="email" name="email" id="email" required="required" value="{{ .Email }}" placeholder="{{( pgettext "Email" "input" )}}">
|
</fieldset>
|
||||||
<label for="email">{{( pgettext "Email" "input" )}}</label>
|
<fieldset class="full-width">
|
||||||
</div>
|
<legend>{{( pgettext "Password Change" "title" )}}</legend>
|
||||||
</fieldset>
|
|
||||||
<fieldset class="full-width">
|
{{ template "input-field" .Password }}
|
||||||
<legend>{{( pgettext "Password Change" "title" )}}</legend>
|
{{ template "input-field" .PasswordConfirm }}
|
||||||
|
</fieldset>
|
||||||
<div class="input">
|
|
||||||
<input type="password" name="password" id="password" value="{{ .Password }}" placeholder="{{( pgettext "Password" "input" )}}">
|
<fieldset>
|
||||||
<label for="password">{{( pgettext "Password" "input" )}}</label>
|
<legend id="language-legend">{{( pgettext "Language" "title" )}}</legend>
|
||||||
</div>
|
|
||||||
|
{{ template "select-field" .Language }}
|
||||||
<div class="input">
|
|
||||||
<input type="password" name="password_confirm" id="password_confirm" value="{{ .PasswordConfirm }}" placeholder="{{( pgettext "Password Confirmation" "input" )}}">
|
<button type="submit">{{( pgettext "Save changes" "action" )}}</button>
|
||||||
<label for="password_confirm">{{( pgettext "Password Confirmation" "input" )}}</label>
|
</fieldset>
|
||||||
</div>
|
</form>
|
||||||
</fieldset>
|
</section>
|
||||||
|
|
||||||
<fieldset>
|
|
||||||
<legend id="language-legend">{{( pgettext "Language" "input" )}}</legend>
|
|
||||||
|
|
||||||
<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 }}
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
|
{{ 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>
|
||||||
@ -85,7 +89,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" name="DELETE"/>
|
<input type="hidden" name="_method" value="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>
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="{{ currentLocale }}">
|
||||||
<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>{{( pgettext "Login" "title" )}} — Numerus</title>
|
<title>{{ template "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">
|
||||||
|
Loading…
x
Reference in New Issue
Block a user