Add a period filter for the dashboard
I do not yet know whether Oriol wants a YTD or MAT period, and i went for the easiest for me: everything is MAT.
This commit is contained in:
parent
f68aba1387
commit
987a99e0df
|
@ -5,6 +5,13 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
YearPeriod = "year"
|
||||||
|
QuarterPeriod = "quarter"
|
||||||
|
MonthPeriod = "month"
|
||||||
|
YesteryearPeriod = "yesteryear"
|
||||||
|
)
|
||||||
|
|
||||||
type DashboardPage struct {
|
type DashboardPage struct {
|
||||||
Sales string
|
Sales string
|
||||||
Income string
|
Income string
|
||||||
|
@ -12,10 +19,30 @@ type DashboardPage struct {
|
||||||
VAT string
|
VAT string
|
||||||
IRPF string
|
IRPF string
|
||||||
NetIncome string
|
NetIncome string
|
||||||
|
Filters *dashboardFilterForm
|
||||||
}
|
}
|
||||||
|
|
||||||
func ServeDashboard(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
func ServeDashboard(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||||
company := mustGetCompany(r)
|
company := mustGetCompany(r)
|
||||||
|
locale := getLocale(r)
|
||||||
|
filters := newDashboardFilterForm(locale, company)
|
||||||
|
if err := filters.Parse(r); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
periodStart := "30 DAYS"
|
||||||
|
periodEnd := "0 DAYS"
|
||||||
|
switch filters.Period.Selected {
|
||||||
|
case YearPeriod:
|
||||||
|
periodStart = "1 YEAR"
|
||||||
|
case QuarterPeriod:
|
||||||
|
periodStart = "3 MONTHS"
|
||||||
|
case YesteryearPeriod:
|
||||||
|
periodStart = "2 YEARS"
|
||||||
|
periodEnd = "1 YEAR"
|
||||||
|
case "":
|
||||||
|
filters.Period.Selected = MonthPeriod
|
||||||
|
}
|
||||||
conn := getConn(r)
|
conn := getConn(r)
|
||||||
rows := conn.MustQuery(r.Context(), `
|
rows := conn.MustQuery(r.Context(), `
|
||||||
select to_price(0, decimal_digits) as sales
|
select to_price(0, decimal_digits) as sales
|
||||||
|
@ -29,11 +56,13 @@ func ServeDashboard(w http.ResponseWriter, r *http.Request, _ httprouter.Params)
|
||||||
select company_id, sum(total)::integer as total
|
select company_id, sum(total)::integer as total
|
||||||
from invoice
|
from invoice
|
||||||
join invoice_amount using (invoice_id)
|
join invoice_amount using (invoice_id)
|
||||||
|
where invoice_date between CURRENT_DATE - $2::interval and CURRENT_DATE - $3::interval
|
||||||
group by company_id
|
group by company_id
|
||||||
) as invoice using (company_id)
|
) as invoice using (company_id)
|
||||||
left join (
|
left join (
|
||||||
select company_id, sum(amount)::integer as total
|
select company_id, sum(amount)::integer as total
|
||||||
from expense
|
from expense
|
||||||
|
where invoice_date between CURRENT_DATE - $2::interval and CURRENT_DATE - $3::interval
|
||||||
group by company_id
|
group by company_id
|
||||||
) as expense using (company_id)
|
) as expense using (company_id)
|
||||||
left join (
|
left join (
|
||||||
|
@ -44,14 +73,17 @@ func ServeDashboard(w http.ResponseWriter, r *http.Request, _ httprouter.Params)
|
||||||
join invoice_tax_amount using (invoice_id)
|
join invoice_tax_amount using (invoice_id)
|
||||||
join tax using (tax_id)
|
join tax using (tax_id)
|
||||||
join tax_class using (tax_class_id)
|
join tax_class using (tax_class_id)
|
||||||
|
where invoice_date between CURRENT_DATE - $2::interval and CURRENT_DATE - $3::interval
|
||||||
group by invoice.company_id
|
group by invoice.company_id
|
||||||
) as tax using (company_id)
|
) as tax using (company_id)
|
||||||
join currency using (currency_code)
|
join currency using (currency_code)
|
||||||
where company_id = $1
|
where company_id = $1
|
||||||
`, company.Id)
|
`, company.Id, periodStart, periodEnd)
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
dashboard := &DashboardPage{}
|
dashboard := &DashboardPage{
|
||||||
|
Filters: filters,
|
||||||
|
}
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
if err := rows.Scan(&dashboard.Sales, &dashboard.Income, &dashboard.Expenses, &dashboard.VAT, &dashboard.IRPF, &dashboard.NetIncome); err != nil {
|
if err := rows.Scan(&dashboard.Sales, &dashboard.Income, &dashboard.Expenses, &dashboard.VAT, &dashboard.IRPF, &dashboard.NetIncome); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@ -62,3 +94,47 @@ func ServeDashboard(w http.ResponseWriter, r *http.Request, _ httprouter.Params)
|
||||||
}
|
}
|
||||||
mustRenderMainTemplate(w, r, "dashboard.gohtml", dashboard)
|
mustRenderMainTemplate(w, r, "dashboard.gohtml", dashboard)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type dashboardFilterForm struct {
|
||||||
|
locale *Locale
|
||||||
|
company *Company
|
||||||
|
Period *RadioField
|
||||||
|
}
|
||||||
|
|
||||||
|
func newDashboardFilterForm(locale *Locale, company *Company) *dashboardFilterForm {
|
||||||
|
return &dashboardFilterForm{
|
||||||
|
locale: locale,
|
||||||
|
company: company,
|
||||||
|
Period: &RadioField{
|
||||||
|
Name: "period",
|
||||||
|
Selected: MonthPeriod,
|
||||||
|
Label: pgettext("input", "Period", locale),
|
||||||
|
Options: []*RadioOption{
|
||||||
|
{
|
||||||
|
Label: pgettext("period option", "Year", locale),
|
||||||
|
Value: YearPeriod,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Label: pgettext("period option", "Quarter", locale),
|
||||||
|
Value: QuarterPeriod,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Label: pgettext("period option", "Month", locale),
|
||||||
|
Value: MonthPeriod,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Label: pgettext("period option", "Previous Year", locale),
|
||||||
|
Value: YesteryearPeriod,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (form *dashboardFilterForm) Parse(r *http.Request) error {
|
||||||
|
if err := r.ParseForm(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
form.Period.FillValue(r)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
53
pkg/form.go
53
pkg/form.go
|
@ -217,6 +217,59 @@ func mustGetCountryOptions(ctx context.Context, conn *Conn, locale *Locale) []*S
|
||||||
return MustGetOptions(ctx, conn, "select country.country_code, coalesce(i18n.name, country.name) as l10n_name from country left join country_i18n as i18n on country.country_code = i18n.country_code and i18n.lang_tag = $1 order by l10n_name", locale.Language)
|
return MustGetOptions(ctx, conn, "select country.country_code, coalesce(i18n.name, country.name) as l10n_name from country left join country_i18n as i18n on country.country_code = i18n.country_code and i18n.lang_tag = $1 order by l10n_name", locale.Language)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RadioOption struct {
|
||||||
|
Value string
|
||||||
|
Label string
|
||||||
|
}
|
||||||
|
|
||||||
|
type RadioField struct {
|
||||||
|
Name string
|
||||||
|
Label string
|
||||||
|
Selected string
|
||||||
|
Options []*RadioOption
|
||||||
|
Attributes []template.HTMLAttr
|
||||||
|
Required bool
|
||||||
|
Errors []error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (field *RadioField) Scan(value interface{}) error {
|
||||||
|
if value == nil {
|
||||||
|
field.Selected = ""
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
field.Selected = fmt.Sprintf("%v", value)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (field *RadioField) Value() (driver.Value, error) {
|
||||||
|
return field.Selected, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (field *RadioField) String() string {
|
||||||
|
return field.Selected
|
||||||
|
}
|
||||||
|
|
||||||
|
func (field *RadioField) FillValue(r *http.Request) {
|
||||||
|
field.Selected = strings.TrimSpace(r.FormValue(field.Name))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (field *RadioField) IsSelected(v string) bool {
|
||||||
|
return field.Selected == v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (field *RadioField) FindOption(value string) *RadioOption {
|
||||||
|
for _, option := range field.Options {
|
||||||
|
if option.Value == value {
|
||||||
|
return option
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (field *RadioField) isValidOption(selected string) bool {
|
||||||
|
return field.FindOption(selected) != nil
|
||||||
|
}
|
||||||
|
|
||||||
type FileField struct {
|
type FileField struct {
|
||||||
Name string
|
Name string
|
||||||
Label string
|
Label string
|
||||||
|
|
46
po/ca.po
46
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-05-16 14:50+0200\n"
|
"POT-Creation-Date: 2023-05-17 11:59+0200\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"
|
||||||
|
@ -104,8 +104,9 @@ msgctxt "action"
|
||||||
msgid "Download invoices"
|
msgid "Download invoices"
|
||||||
msgstr "Descarrega factures"
|
msgstr "Descarrega factures"
|
||||||
|
|
||||||
#: web/template/invoices/index.gohtml:40 web/template/contacts/index.gohtml:32
|
#: web/template/invoices/index.gohtml:40 web/template/dashboard.gohtml:22
|
||||||
#: web/template/expenses/index.gohtml:34 web/template/products/index.gohtml:32
|
#: web/template/contacts/index.gohtml:32 web/template/expenses/index.gohtml:34
|
||||||
|
#: web/template/products/index.gohtml:32
|
||||||
msgctxt "action"
|
msgctxt "action"
|
||||||
msgid "Filter"
|
msgid "Filter"
|
||||||
msgstr "Filtra"
|
msgstr "Filtra"
|
||||||
|
@ -219,7 +220,7 @@ msgid "Edit invoice"
|
||||||
msgstr "Edita factura"
|
msgstr "Edita factura"
|
||||||
|
|
||||||
#: web/template/form.gohtml:36
|
#: web/template/form.gohtml:36
|
||||||
msgctxt "label"
|
msgctxt "input"
|
||||||
msgid "(Max. %s)"
|
msgid "(Max. %s)"
|
||||||
msgstr "(Màx. %s)"
|
msgstr "(Màx. %s)"
|
||||||
|
|
||||||
|
@ -228,32 +229,32 @@ msgctxt "title"
|
||||||
msgid "Dashboard"
|
msgid "Dashboard"
|
||||||
msgstr "Tauler"
|
msgstr "Tauler"
|
||||||
|
|
||||||
#: web/template/dashboard.gohtml:14
|
#: web/template/dashboard.gohtml:27
|
||||||
msgctxt "term"
|
msgctxt "term"
|
||||||
msgid "Sales"
|
msgid "Sales"
|
||||||
msgstr "Vendes"
|
msgstr "Vendes"
|
||||||
|
|
||||||
#: web/template/dashboard.gohtml:18
|
#: web/template/dashboard.gohtml:31
|
||||||
msgctxt "term"
|
msgctxt "term"
|
||||||
msgid "Income"
|
msgid "Income"
|
||||||
msgstr "Ingressos"
|
msgstr "Ingressos"
|
||||||
|
|
||||||
#: web/template/dashboard.gohtml:22
|
#: web/template/dashboard.gohtml:35
|
||||||
msgctxt "term"
|
msgctxt "term"
|
||||||
msgid "Expenses"
|
msgid "Expenses"
|
||||||
msgstr "Despeses"
|
msgstr "Despeses"
|
||||||
|
|
||||||
#: web/template/dashboard.gohtml:26
|
#: web/template/dashboard.gohtml:39
|
||||||
msgctxt "term"
|
msgctxt "term"
|
||||||
msgid "VAT"
|
msgid "VAT"
|
||||||
msgstr "IVA"
|
msgstr "IVA"
|
||||||
|
|
||||||
#: web/template/dashboard.gohtml:30
|
#: web/template/dashboard.gohtml:43
|
||||||
msgctxt "term"
|
msgctxt "term"
|
||||||
msgid "IRPF"
|
msgid "IRPF"
|
||||||
msgstr "IRPF"
|
msgstr "IRPF"
|
||||||
|
|
||||||
#: web/template/dashboard.gohtml:34
|
#: web/template/dashboard.gohtml:47
|
||||||
msgctxt "term"
|
msgctxt "term"
|
||||||
msgid "Net Income"
|
msgid "Net Income"
|
||||||
msgstr "Ingressos nets"
|
msgstr "Ingressos nets"
|
||||||
|
@ -738,6 +739,31 @@ msgstr "La confirmació no és igual a la contrasenya."
|
||||||
msgid "Selected language is not valid."
|
msgid "Selected language is not valid."
|
||||||
msgstr "Heu seleccionat un idioma que no és vàlid."
|
msgstr "Heu seleccionat un idioma que no és vàlid."
|
||||||
|
|
||||||
|
#: pkg/dashboard.go:112
|
||||||
|
msgctxt "input"
|
||||||
|
msgid "Period"
|
||||||
|
msgstr "Període"
|
||||||
|
|
||||||
|
#: pkg/dashboard.go:115
|
||||||
|
msgctxt "period option"
|
||||||
|
msgid "Year"
|
||||||
|
msgstr "Any"
|
||||||
|
|
||||||
|
#: pkg/dashboard.go:119
|
||||||
|
msgctxt "period option"
|
||||||
|
msgid "Quarter"
|
||||||
|
msgstr "Trimestre"
|
||||||
|
|
||||||
|
#: pkg/dashboard.go:123
|
||||||
|
msgctxt "period option"
|
||||||
|
msgid "Month"
|
||||||
|
msgstr "Mes"
|
||||||
|
|
||||||
|
#: pkg/dashboard.go:127
|
||||||
|
msgctxt "period option"
|
||||||
|
msgid "Previous Year"
|
||||||
|
msgstr "Any anterior"
|
||||||
|
|
||||||
#: pkg/expenses.go:129
|
#: pkg/expenses.go:129
|
||||||
msgid "Select a contact."
|
msgid "Select a contact."
|
||||||
msgstr "Escolliu un contacte."
|
msgstr "Escolliu un contacte."
|
||||||
|
|
46
po/es.po
46
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-05-16 14:50+0200\n"
|
"POT-Creation-Date: 2023-05-17 11:59+0200\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"
|
||||||
|
@ -104,8 +104,9 @@ msgctxt "action"
|
||||||
msgid "Download invoices"
|
msgid "Download invoices"
|
||||||
msgstr "Descargar facturas"
|
msgstr "Descargar facturas"
|
||||||
|
|
||||||
#: web/template/invoices/index.gohtml:40 web/template/contacts/index.gohtml:32
|
#: web/template/invoices/index.gohtml:40 web/template/dashboard.gohtml:22
|
||||||
#: web/template/expenses/index.gohtml:34 web/template/products/index.gohtml:32
|
#: web/template/contacts/index.gohtml:32 web/template/expenses/index.gohtml:34
|
||||||
|
#: web/template/products/index.gohtml:32
|
||||||
msgctxt "action"
|
msgctxt "action"
|
||||||
msgid "Filter"
|
msgid "Filter"
|
||||||
msgstr "Filtrar"
|
msgstr "Filtrar"
|
||||||
|
@ -219,7 +220,7 @@ msgid "Edit invoice"
|
||||||
msgstr "Editar factura"
|
msgstr "Editar factura"
|
||||||
|
|
||||||
#: web/template/form.gohtml:36
|
#: web/template/form.gohtml:36
|
||||||
msgctxt "label"
|
msgctxt "input"
|
||||||
msgid "(Max. %s)"
|
msgid "(Max. %s)"
|
||||||
msgstr "(Máx. %s)"
|
msgstr "(Máx. %s)"
|
||||||
|
|
||||||
|
@ -228,32 +229,32 @@ msgctxt "title"
|
||||||
msgid "Dashboard"
|
msgid "Dashboard"
|
||||||
msgstr "Panel"
|
msgstr "Panel"
|
||||||
|
|
||||||
#: web/template/dashboard.gohtml:14
|
#: web/template/dashboard.gohtml:27
|
||||||
msgctxt "term"
|
msgctxt "term"
|
||||||
msgid "Sales"
|
msgid "Sales"
|
||||||
msgstr "Ventas"
|
msgstr "Ventas"
|
||||||
|
|
||||||
#: web/template/dashboard.gohtml:18
|
#: web/template/dashboard.gohtml:31
|
||||||
msgctxt "term"
|
msgctxt "term"
|
||||||
msgid "Income"
|
msgid "Income"
|
||||||
msgstr "Ingresos"
|
msgstr "Ingresos"
|
||||||
|
|
||||||
#: web/template/dashboard.gohtml:22
|
#: web/template/dashboard.gohtml:35
|
||||||
msgctxt "term"
|
msgctxt "term"
|
||||||
msgid "Expenses"
|
msgid "Expenses"
|
||||||
msgstr "Gastos"
|
msgstr "Gastos"
|
||||||
|
|
||||||
#: web/template/dashboard.gohtml:26
|
#: web/template/dashboard.gohtml:39
|
||||||
msgctxt "term"
|
msgctxt "term"
|
||||||
msgid "VAT"
|
msgid "VAT"
|
||||||
msgstr "IVA"
|
msgstr "IVA"
|
||||||
|
|
||||||
#: web/template/dashboard.gohtml:30
|
#: web/template/dashboard.gohtml:43
|
||||||
msgctxt "term"
|
msgctxt "term"
|
||||||
msgid "IRPF"
|
msgid "IRPF"
|
||||||
msgstr "IRPF"
|
msgstr "IRPF"
|
||||||
|
|
||||||
#: web/template/dashboard.gohtml:34
|
#: web/template/dashboard.gohtml:47
|
||||||
msgctxt "term"
|
msgctxt "term"
|
||||||
msgid "Net Income"
|
msgid "Net Income"
|
||||||
msgstr "Ingresos netos"
|
msgstr "Ingresos netos"
|
||||||
|
@ -738,6 +739,31 @@ msgstr "La confirmación no corresponde con la contraseña."
|
||||||
msgid "Selected language is not valid."
|
msgid "Selected language is not valid."
|
||||||
msgstr "Habéis escogido un idioma que no es válido."
|
msgstr "Habéis escogido un idioma que no es válido."
|
||||||
|
|
||||||
|
#: pkg/dashboard.go:112
|
||||||
|
msgctxt "input"
|
||||||
|
msgid "Period"
|
||||||
|
msgstr "Periodo"
|
||||||
|
|
||||||
|
#: pkg/dashboard.go:115
|
||||||
|
msgctxt "period option"
|
||||||
|
msgid "Year"
|
||||||
|
msgstr "Año"
|
||||||
|
|
||||||
|
#: pkg/dashboard.go:119
|
||||||
|
msgctxt "period option"
|
||||||
|
msgid "Quarter"
|
||||||
|
msgstr "Trimestre"
|
||||||
|
|
||||||
|
#: pkg/dashboard.go:123
|
||||||
|
msgctxt "period option"
|
||||||
|
msgid "Month"
|
||||||
|
msgstr "Mes"
|
||||||
|
|
||||||
|
#: pkg/dashboard.go:127
|
||||||
|
msgctxt "period option"
|
||||||
|
msgid "Previous Year"
|
||||||
|
msgstr "Año anterior"
|
||||||
|
|
||||||
#: pkg/expenses.go:129
|
#: pkg/expenses.go:129
|
||||||
msgid "Select a contact."
|
msgid "Select a contact."
|
||||||
msgstr "Escoged un contacto"
|
msgstr "Escoged un contacto"
|
||||||
|
|
|
@ -309,9 +309,12 @@ main {
|
||||||
}
|
}
|
||||||
|
|
||||||
.input {
|
.input {
|
||||||
|
margin-top: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input:not(.radio) {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-top: 2rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type="text"], input[type="search"], input[type="password"], input[type="email"], input[type="tel"], input[type="url"], input[type="number"], input[type="date"], select, textarea {
|
input[type="text"], input[type="search"], input[type="password"], input[type="email"], input[type="tel"], input[type="url"], input[type="number"], input[type="date"], select, textarea {
|
||||||
|
@ -334,7 +337,7 @@ input.width-2x {
|
||||||
color: transparent;
|
color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input label, .input input:focus ~ label {
|
.input:not(.radio) label, .input:not(.radio) input:focus ~ label {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
@ -365,7 +368,7 @@ input.width-2x {
|
||||||
content: " (opcional)"
|
content: " (opcional)"
|
||||||
}
|
}
|
||||||
|
|
||||||
.input label, .input input:focus ~ label {
|
.input:not(.radio) label, .input:not(.radio) input:focus ~ label {
|
||||||
background-color: var(--numerus--background-color);
|
background-color: var(--numerus--background-color);
|
||||||
top: -.9rem;
|
top: -.9rem;
|
||||||
left: 2rem;
|
left: 2rem;
|
||||||
|
@ -384,17 +387,23 @@ input.width-2x {
|
||||||
|
|
||||||
fieldset {
|
fieldset {
|
||||||
border: none;
|
border: none;
|
||||||
padding: 2rem 0 0;
|
|
||||||
margin-top: 3rem;
|
|
||||||
border-top: 1px solid var(--numerus--color--light-gray);
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fieldset:not(.radio) {
|
||||||
|
padding: 2rem 0 0;
|
||||||
|
margin-top: 3rem;
|
||||||
|
border-top: 1px solid var(--numerus--color--light-gray);
|
||||||
|
}
|
||||||
|
|
||||||
legend {
|
legend {
|
||||||
float: left;
|
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset:not(.radio) {
|
||||||
|
float: left;
|
||||||
margin-bottom: 3rem;
|
margin-bottom: 3rem;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
@ -615,7 +624,7 @@ main > nav {
|
||||||
}
|
}
|
||||||
|
|
||||||
[is="numerus-multiselect"] .tags, [is="numerus-tags"] .tags,
|
[is="numerus-multiselect"] .tags, [is="numerus-tags"] .tags,
|
||||||
[is="numerus-multiselect"] .options, [is="numerus-product-search"] .options {
|
[is="numerus-multiselect"] .options, [is="numerus-product-search"] .options {
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
color: var(--numerus--text-color);
|
color: var(--numerus--text-color);
|
||||||
|
@ -701,8 +710,8 @@ main > nav {
|
||||||
padding: 1rem 2rem;
|
padding: 1rem 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
[is="numerus-product-search"] .options li:hover,[is="numerus-product-search"] .options .highlight,
|
[is="numerus-product-search"] .options li:hover, [is="numerus-product-search"] .options .highlight,
|
||||||
[is="numerus-multiselect"] .options li:hover,[is="numerus-multiselect"] .options .highlight {
|
[is="numerus-multiselect"] .options li:hover, [is="numerus-multiselect"] .options .highlight {
|
||||||
background-color: var(--numerus--color--light-gray);
|
background-color: var(--numerus--color--light-gray);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -825,6 +834,23 @@ div[x-data="snackbar"] div[role="alert"].enter.end, div[x-data="snackbar"] div[r
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Dashboard */
|
/* Dashboard */
|
||||||
|
#dashboard-filters {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
#dashboard-filters .radio {
|
||||||
|
margin-top: 0;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#dashboard-filters .radio label {
|
||||||
|
text-transform: lowercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
#dashboard-filters legend {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
#income-statement {
|
#income-statement {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
@ -834,7 +860,7 @@ div[x-data="snackbar"] div[role="alert"].enter.end, div[x-data="snackbar"] div[r
|
||||||
#income-statement div {
|
#income-statement div {
|
||||||
display: block;
|
display: block;
|
||||||
padding: 2rem;
|
padding: 2rem;
|
||||||
width: calc(100%/3);
|
width: calc(100% / 3);
|
||||||
min-width: 33rem;
|
min-width: 33rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,19 @@
|
||||||
|
|
||||||
{{ define "content" }}
|
{{ define "content" }}
|
||||||
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.DashboardPage*/ -}}
|
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.DashboardPage*/ -}}
|
||||||
|
<div aria-label="{{( pgettext "Filters" "title" )}}">
|
||||||
|
<form method="GET" action="{{ companyURI "/"}}"
|
||||||
|
id="dashboard-filters"
|
||||||
|
data-hx-target="main"
|
||||||
|
data-hx-boost="true"
|
||||||
|
data-hx-trigger="change,submit"
|
||||||
|
>
|
||||||
|
{{ with .Filters }}
|
||||||
|
{{ template "radio-field" .Period }}
|
||||||
|
{{ end }}
|
||||||
|
<button type="submit">{{( pgettext "Filter" "action" )}}</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
<dl id="income-statement">
|
<dl id="income-statement">
|
||||||
<div>
|
<div>
|
||||||
<dt>{{ (pgettext "Sales" "term") }}</dt>
|
<dt>{{ (pgettext "Sales" "term") }}</dt>
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.FileField*/ -}}
|
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.FileField*/ -}}
|
||||||
<div class="input{{ if .Errors }} has-errors{{ end }}">
|
<div class="input{{ if .Errors }} has-errors{{ end }}">
|
||||||
<input type="file" name="{{ .Name }}" id="{{ .Name }}-field" placeholder="{{ .Label }}">
|
<input type="file" name="{{ .Name }}" id="{{ .Name }}-field" placeholder="{{ .Label }}">
|
||||||
<label for="{{ .Name }}-field">{{ .Label }}{{ if gt .MaxSize 0 }} {{printf (pgettext "(Max. %s)" "label") (.MaxSize|humanizeBytes) }}{{ end }}</label>
|
<label for="{{ .Name }}-field">{{ .Label }}{{ if gt .MaxSize 0 }} {{printf (pgettext "(Max. %s)" "input") (.MaxSize|humanizeBytes) }}{{ end }}</label>
|
||||||
{{- if .Errors }}
|
{{- if .Errors }}
|
||||||
<ul>
|
<ul>
|
||||||
{{- range $error := .Errors }}
|
{{- range $error := .Errors }}
|
||||||
|
@ -125,6 +125,23 @@
|
||||||
</fieldset>
|
</fieldset>
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
||||||
|
{{ define "radio-field" -}}
|
||||||
|
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.RadioField*/ -}}
|
||||||
|
<fieldset id="{{ .Name}}-field" class="input radio{{ if .Errors }} has-errors{{ end }}">
|
||||||
|
<legend>{{ .Label }}</legend>
|
||||||
|
{{- range $option := .Options }}
|
||||||
|
<label><input type="radio" name="{{$.Name}}" value="{{.Value}}" {{- if $.IsSelected .Value }} checked="checked"{{ end }}> {{.Label}}</label>
|
||||||
|
{{- end }}
|
||||||
|
{{- if .Errors }}
|
||||||
|
<ul>
|
||||||
|
{{- range $error := .Errors }}
|
||||||
|
<li>{{ . }}</li>
|
||||||
|
{{- end }}
|
||||||
|
</ul>
|
||||||
|
{{- end }}
|
||||||
|
</fieldset>
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
{{ define "invoice-product-form" -}}
|
{{ define "invoice-product-form" -}}
|
||||||
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.invoiceProductForm*/ -}}
|
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.invoiceProductForm*/ -}}
|
||||||
<fieldset class="new-invoice-product"
|
<fieldset class="new-invoice-product"
|
||||||
|
|
Loading…
Reference in New Issue