# BI Dashboard
A flat, server-free dashboard for the Brandize BI webhook
(`Summe je Abgangsland`). It shows the gross revenue (Brutto inkl. USt) per
departure country for a selected period, with KPI cards, a bar chart and a
detail table.
## Run
Just open `index.html` in a browser (double-click it). No server, no build step.
The page calls the webhook directly:
```
https://brandize.app.n8n.cloud/webhook/bi-dashboard
```
via a **POST** request with a JSON body:
```json
{ "query": "Summe je Abgangsland", "month": "2026-SEP" }
```
The webhook sends CORS headers (it reflects the request `Origin`, including
`null` for `file://`), so a direct browser call works.
## Navigation
The left sidebar groups reports by topic:
- **Amazon** → *Umsatz je Abgangsland* (Brutto inkl. USt je Abgangsland)
- **Amazon** → *VAT Summary* (USt-Übersicht je Land)
- **ODOO** → *Rechnungen pro Monat* (Pivot über Odoo-Rechnungen)
- **ODOO** → *FIFO Lager Bewertung* (Lagerwert FIFO)
- **ODOO** → *Produkt Materialkosten* (Materialkosten je Produkt)
- **Gesamt** → *Alle Rechnungen pro Monat* (PivotTable.js-Experiment)
- **Gesamt** → *Quartal Schweiz* (AMAZON + ODOO, Lieferung in die Schweiz)
All groups start collapsed, so no submenu is visible when the page opens.
Clicking a topic expands its submenu; clicking a submenu item switches the
view.
## Authentication
The webhooks are protected with HTTP Basic Auth. The dashboard asks for the
credentials when they are needed: the first request is sent without them, and
when the webhook answers `401`, a login prompt appears. The entered
credentials are sent as an `Authorization: Basic ...` header on every request
and kept in `sessionStorage` for the rest of the browser session (so a reload
does not prompt again). If the webhook rejects them (`401`), the prompt
reappears. Storage is best-effort: on `file://` it may be unavailable, in which
case credentials are kept in memory only.
## CSV export
The header has a **CSV** button that downloads the data of the currently
displayed dashboard, as a semicolon-delimited UTF-8 file (with BOM) including a
Gesamt row:
- *Umsatz je Abgangsland* → `umsatz-je-abgangsland_YYYY-MMM.csv`
- *VAT Summary* → `vat-summary_YYYY-MMM.csv`
- *Rechnungen pro Monat* → `odoo-rechnungen_YYYY-MMM.csv`
- *Alle Rechnungen pro Monat* → `gesamt-rechnungen-pivot_YYYY-MMM.csv`
- *Quartal Schweiz* → `schweiz-quartal_YYYY-QN.csv`
- *FIFO Lager Bewertung* → `fifo-lager_YYYY-MMM.csv`
- *Produkt Materialkosten* → `materialkosten_YYYY-MMM.csv`
The button is disabled while no dataset is loaded for the current dashboard.
## Filter
A shared filter bar sits above every dashboard (labelled **Filter** with a
funnel icon and the hint *gilt für alle Dashboards*). It holds the **Zeitraum**
month + year selector (e.g. `2026-SEP`) and the reload button, and stays visible
across all views. The previous month is preselected. The selected period is
sent to the webhook in the POST body as `month` (e.g. `2026-SEP`), matching the
`%(month)s` parameter used in `../sql/*.sql`.
The invoice dashboards (*ODOO Rechnungen pro Monat*, *Alle Rechnungen pro Monat*
and *Quartal Schweiz*) have an additional, dashboard-specific filter row. *ODOO
Rechnungen pro Monat* filters by **Tag**, **Bestellnummer**, **Lieferland** and
**Rechnungsnummer**; *Alle Rechnungen pro Monat* and *Quartal Schweiz* add a
**Quelle** field. Filtering re-renders the KPIs, chart and pivot live.
For *Quartal Schweiz* the shared **Zeitraum** control shows a **quarter**
selector (Q1–Q4) instead of the month selector; the year selector is shared.
## Pivot (Experiment)
The three invoice dashboards (*ODOO Rechnungen pro Monat*, *Alle Rechnungen pro
Monat* and *Quartal Schweiz*) render their data as an interactive
[PivotTable.js](https://pivottable.js.org/) pivot instead of a detail table.
The pivot's rows, columns, values and aggregator are freely configurable in the
pivotUI control panel; the renderer is fixed to *Table*. Each dashboard has its
own filter row and **Vorlage** dropdown with presets:
- *ODOO Rechnungen pro Monat*: *Standard* (**Rechnungsland** rows, no column
group, **Sum(Gesamtpreis brutto)**) and *Zahlart*.
- *Alle Rechnungen pro Monat* and *Quartal Schweiz*: *Standard* (**Quelle**
rows, no column group, **Sum(Gesamtpreis brutto)**) and *Rechnungsland
(Spalten)* (adds **Rechnungsland** as the column group).
*Quartal Schweiz* is filtered to invoices with a **delivery into Switzerland**.
Applying a preset also resets the filters. The chosen layout is kept while you
type in the filters.
It loads jQuery, jQuery UI and PivotTable.js from public CDNs, so — unlike the
rest of the app — it needs an **internet connection** to render; offline it shows
a short message instead. To vendor the libraries locally, download them next to
`index.html` and change the ``/`