146 lines
7.0 KiB
Markdown
146 lines
7.0 KiB
Markdown
# Web admin and analytics
|
|
|
|
An Axum SSR/HTMX web frontend for the existing komp_ac gRPC backend. It provides
|
|
the browser version of the client admin panel and the ECharts analytics view.
|
|
The browser never connects to gRPC directly: Axum authenticates with an
|
|
HTTP-only cookie, creates typed Tonic requests, and returns full HTML pages or
|
|
HTMX fragments.
|
|
|
|
The backend remains the authority; this crate does not add another HTTP API to it.
|
|
|
|
## Run
|
|
|
|
The web UI is a library started by the main server; it is no longer a standalone
|
|
binary. From the parent workspace, start the server normally:
|
|
|
|
```sh
|
|
cargo run -p server -- server
|
|
```
|
|
|
|
Open <http://127.0.0.1:3000/login> to log in. The admin panel is at
|
|
<http://127.0.0.1:3000/admin> and analytics remains at <http://127.0.0.1:3000>.
|
|
The access token is kept in an HTTP-only cookie. The default gRPC endpoint is
|
|
`http://[::1]:50051`. Both addresses can be changed:
|
|
|
|
```sh
|
|
ANALYTICS_GRPC_ENDPOINT=http://127.0.0.1:50051 \
|
|
LISTEN_ADDRESS=127.0.0.1:8080 \
|
|
cargo run -p server -- server
|
|
```
|
|
|
|
## Structure
|
|
|
|
HTML lives in `.html` files, never in Rust strings. Templates are
|
|
[askama](https://docs.rs/askama): compiled at build time, type-checked against
|
|
their struct, and HTML-escaped automatically.
|
|
|
|
**`templates/` mirrors `src/` directory for directory.** The Rust for a page
|
|
and the markup for that page live at the same path in the two trees, and
|
|
`src/pages/` itself matches the client crate — the same page names, the same
|
|
`loader / logic / state / ui` split. A `#[template(path = …)]` is therefore the
|
|
module's own path, which is also where the file is in `templates/`.
|
|
|
|
```
|
|
src/ templates/
|
|
ui/ ui/
|
|
mod.rs base.html <html>, <head>, navbar
|
|
(Nav, Alert, Notice, navbar.html the one navbar
|
|
ErrorPage, render) form_page.html admin form card layout
|
|
alert.html error/success macros
|
|
alert_fragment.html standalone POST reply
|
|
notice.html one-line inline notice
|
|
error.html full-page load failure
|
|
pages/ pages/
|
|
admin/admin/ admin/admin/
|
|
mod loader logic state ui admin.html
|
|
workspace.html
|
|
add_table/ add_table/
|
|
mod loader logic state ui add_table.html
|
|
add_logic/ add_logic/
|
|
… add_logic.html
|
|
add_validation/ add_validation/
|
|
… field.html rule.html set.html
|
|
shared_fields.html
|
|
analytics/ analytics/
|
|
… analytics.html catalog.html
|
|
profile_options.html query_result.html
|
|
import_export/import/ import_export/import/
|
|
… import.html
|
|
import_export/export/ import_export/export/
|
|
… export.html
|
|
login/ login/
|
|
mod logic state ui login.html
|
|
|
|
static/app.css the only stylesheet, at /static/app.css
|
|
```
|
|
|
|
Every full page extends `ui/base.html`, which links the stylesheet and renders
|
|
`ui/navbar.html`, so the navbar is on every page without a page opting in. The
|
|
six admin forms extend `ui/form_page.html` for the back link and card chrome on
|
|
top of that.
|
|
|
|
Each endpoint maps to exactly one template. The Rust side of the mapping is the
|
|
`#[derive(Template)]` struct in the page's `ui.rs`; the template names its
|
|
endpoint in a comment on line 1.
|
|
|
|
| Endpoint | Page directory | Template |
|
|
| --- | --- | --- |
|
|
| `GET /` | `pages/analytics/` | `analytics.html` |
|
|
| `GET /api/profiles` | `pages/analytics/` | `profile_options.html` |
|
|
| `POST /api/catalog` | `pages/analytics/` | `catalog.html` |
|
|
| `POST /api/query` | `pages/analytics/` | `query_result.html` |
|
|
| `GET /login` | `pages/login/` | `login.html` |
|
|
| `POST /login` | `pages/login/` | sets the cookie, `hx-redirect` |
|
|
| `GET /admin` | `pages/admin/admin/` | `admin.html` |
|
|
| `GET /admin/workspace` | `pages/admin/admin/` | `workspace.html` |
|
|
| `POST /logout` | `pages/admin/admin/` | clears the cookie, `hx-redirect` |
|
|
| `GET /admin/tables/new` | `pages/add_table/` | `add_table.html` |
|
|
| `GET /admin/logic/new` | `pages/add_logic/` | `add_logic.html` |
|
|
| `GET /admin/validation/new` | `pages/add_validation/` | `field.html` |
|
|
| `GET /admin/validation/rules/new` | `pages/add_validation/` | `rule.html` |
|
|
| `GET /admin/validation/sets/new` | `pages/add_validation/` | `set.html` |
|
|
| `GET /admin/import` | `pages/import_export/import/` | `import.html` |
|
|
| `GET /admin/export` | `pages/import_export/export/` | `export.html` |
|
|
|
|
Every form `POST` answers with `ui/alert_fragment.html`, swapped into the page's
|
|
`#submission-status`. Analytics errors use the lighter `ui/notice.html`.
|
|
|
|
Inside a page module the split is the same everywhere, and the same as the
|
|
client: `mod.rs` declares the routes, `loader.rs` calls gRPC and shapes the
|
|
result, `state.rs` holds the view model and form parsing, `logic.rs` is the
|
|
handlers, and `ui.rs` binds state to the templates in that directory.
|
|
|
|
## Admin panel
|
|
|
|
The admin panel mirrors the client workflow with browser-oriented pages:
|
|
|
|
- profile, table, and physical-column browsing;
|
|
- table creation, including indexes, links, money settings, and row labels;
|
|
- Steel table-script creation;
|
|
- field validations, reusable global rules, validation sets, and set application;
|
|
- browser CSV import with live schema validation and chunked bulk insertion;
|
|
- CSV downloads for one or multiple tables;
|
|
- login, role checks, logout, and navigation to analytics.
|
|
|
|
CSV files use the same column-header convention as the client. Multi-table CSV
|
|
files include a table-name header row before the column-name row. Browser files
|
|
are read locally and submitted to Axum; the backend is accessed only through the
|
|
existing `TablesData` gRPC service.
|
|
|
|
## Analytics
|
|
|
|
The first SQL result column is used for category labels. Bar and line charts use
|
|
all remaining columns as numeric series, pie uses the second column, and scatter
|
|
uses the first two columns. Table view displays every returned value.
|
|
|
|
The profile selector is populated automatically through
|
|
`TableDefinition.GetProfileTree`. Choosing a profile fetches its live public
|
|
analytics catalog. The sidebar shows tables, columns, types, and links and can
|
|
create starter queries. Its **LLM schema context** section generates a complete
|
|
text summary that can be copied into an LLM prompt to request a valid analytics
|
|
SQL query.
|
|
|
|
HTMX, Alpine.js, and ECharts are loaded from jsDelivr, so the browser needs
|
|
network access when opening the page. HTMX owns server communication and fragment
|
|
swaps; Alpine is limited to browser-local editor, clipboard, and chart behavior.
|