web routing is POG now

This commit is contained in:
Priec
2026-08-12 23:40:14 +02:00
parent 10c8235420
commit f8e483efa8
28 changed files with 1015 additions and 697 deletions

View File

@@ -1,8 +1,12 @@
//! What the workspace renders, and the wire formats its panels post.
//! What the table-definition pages render, and the wire formats they post.
//!
//! Every panel is a form of its own, and each one carries the selection it
//! acts on in hidden fields, because the workspace is swapped whole on every
//! write: the response is rebuilt from the live profile tree rather than from
//! One state type serves all five pages, because each of them needs the same
//! context: which profile and table is being worked on, and what that table
//! currently is. What differs is which panel the page renders, and `active`
//! is what says so.
//!
//! Every panel carries the selection it acts on in hidden fields, because a
//! write answers with its page re-read from the backend rather than with
//! whatever the browser still had on screen.
use crate::schema::{ColumnCatalog, ColumnDraft};
@@ -38,6 +42,21 @@ impl Selection {
pub(crate) fn query(&self) -> String {
format!("?profile={}&table={}", self.profile, self.table)
}
/// The same, for the profile-wide pages, which have no table to name.
pub(crate) fn profile_query(&self) -> String {
format!("?profile={}", self.profile)
}
/// What the heading calls the scope, so the global one is not shown by its
/// sentinel name.
pub(crate) fn scope_label(&self) -> String {
if self.is_global() {
"Global — all profiles".to_string()
} else {
self.profile.clone()
}
}
}
/// One table in the selected profile, from the profile tree.
@@ -47,7 +66,6 @@ pub(crate) struct TableSummary {
pub table_kind: String,
pub global: bool,
pub depends_on: Vec<String>,
pub row_display_columns: Vec<String>,
}
impl TableSummary {
@@ -62,18 +80,12 @@ impl TableSummary {
#[derive(Clone, Debug)]
pub(crate) struct TableDetailView {
pub id: i64,
pub name: String,
pub table_kind: String,
pub row_display_columns: Vec<String>,
pub columns: Vec<DetailColumn>,
pub scripts: Vec<ScriptView>,
}
impl TableDetailView {
pub(crate) fn is_system(&self) -> bool {
self.table_kind == "system"
}
/// Columns a rename may target. Provenance and renameability are separate:
/// accounting companions remain renameable while protected generated
/// columns do not.
@@ -248,7 +260,6 @@ impl PageInputs {
/// What the templates read.
pub(crate) struct TableDefinitionPageState {
pub nav: crate::ui::Nav,
pub profiles: Vec<String>,
pub selection: Selection,
pub tables: Vec<TableSummary>,
pub detail: Option<TableDetailView>,
@@ -261,22 +272,17 @@ pub(crate) struct TableDefinitionPageState {
pub error: Option<String>,
pub sql: Option<String>,
pub generated: Vec<GeneratedTableView>,
pub permission_object: String,
pub role_permissions: Vec<TableRolePermissions>,
}
pub(crate) struct TableRolePermissions {
pub role: String,
pub actions: Vec<TablePermissionAction>,
}
pub(crate) struct TablePermissionAction {
pub action: String,
pub direct: bool,
pub effective: bool,
/// Which of the pages this is, for the action switcher they share. Set by
/// the handler, because it is the one thing the loader cannot know.
pub active: &'static str,
}
impl TableDefinitionPageState {
/// Whether `name` is the page being rendered. Read by context.html.
pub(crate) fn is(&self, name: &str) -> bool {
self.active == name
}
fn eligible_link_target(&self, table: &TableSummary) -> bool {
table.name != self.selection.table && table.name != "accounts"
}
@@ -363,8 +369,6 @@ mod tests {
fn provenance_and_renameability_are_independent() {
let detail = TableDetailView {
id: 1,
name: "contact".to_string(),
table_kind: "dynamic".to_string(),
row_display_columns: Vec::new(),
scripts: Vec::new(),
columns: vec![