add table is better now
This commit is contained in:
@@ -2,24 +2,154 @@ use askama::Template;
|
||||
|
||||
use crate::ui::{Alert, Nav, render};
|
||||
|
||||
use super::state::AddTablePageState;
|
||||
use super::{
|
||||
draft::{COLUMN_TYPES, CURRENCY_CODES, GTIN_TYPES, TEMPORAL_TYPES},
|
||||
state::AddTablePageState,
|
||||
};
|
||||
|
||||
/// GET /admin/tables/new
|
||||
/// GET /admin/tables/new — the page shell around the builder.
|
||||
#[derive(Template)]
|
||||
#[template(path = "pages/add_table/add_table.html")]
|
||||
struct AddTablePage<'a> {
|
||||
nav: Nav,
|
||||
page: &'a AddTablePageState,
|
||||
column_types: &'static [&'static str],
|
||||
temporal_types: &'static [&'static str],
|
||||
gtin_types: &'static [&'static str],
|
||||
currency_codes: &'static [&'static str],
|
||||
}
|
||||
|
||||
/// POST /admin/tables/builder — the `#builder` swap, which is the same markup
|
||||
/// the page embeds, so one template serves both.
|
||||
#[derive(Template)]
|
||||
#[template(path = "pages/add_table/builder.html")]
|
||||
struct BuilderFragment<'a> {
|
||||
page: &'a AddTablePageState,
|
||||
column_types: &'static [&'static str],
|
||||
temporal_types: &'static [&'static str],
|
||||
gtin_types: &'static [&'static str],
|
||||
}
|
||||
|
||||
pub(crate) fn render_page(page: &AddTablePageState) -> String {
|
||||
render(&AddTablePage {
|
||||
nav: page.nav.clone(),
|
||||
page,
|
||||
column_types: COLUMN_TYPES,
|
||||
temporal_types: TEMPORAL_TYPES,
|
||||
gtin_types: GTIN_TYPES,
|
||||
currency_codes: CURRENCY_CODES,
|
||||
})
|
||||
}
|
||||
|
||||
/// POST /admin/tables — the #submission-status swap.
|
||||
pub(crate) fn render_builder(page: &AddTablePageState) -> String {
|
||||
render(&BuilderFragment {
|
||||
page,
|
||||
column_types: COLUMN_TYPES,
|
||||
temporal_types: TEMPORAL_TYPES,
|
||||
gtin_types: GTIN_TYPES,
|
||||
})
|
||||
}
|
||||
|
||||
/// Used when the page itself cannot be loaded (auth or backend failure).
|
||||
pub(crate) fn render_submission_error(message: &str) -> String {
|
||||
render(&Alert::error("Could not create the table", message))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::pages::add_table::draft::{ColumnDefinition, LinkMode, MoneyMode, TableDraft};
|
||||
|
||||
fn page() -> AddTablePageState {
|
||||
let mut draft = TableDraft::new();
|
||||
draft.profile_name = "billing".to_string();
|
||||
draft.table_name = "invoice".to_string();
|
||||
draft.columns.push(ColumnDefinition {
|
||||
name: "number".to_string(),
|
||||
data_type: "text".to_string(),
|
||||
indexed: true,
|
||||
quantity_ledger: false,
|
||||
money_mode: MoneyMode::Exact,
|
||||
});
|
||||
draft.set_available_relation_tables(vec!["customer".to_string()]);
|
||||
draft.cycle_link_mode(0);
|
||||
draft.toggle_row_display_candidate(1);
|
||||
|
||||
AddTablePageState {
|
||||
nav: Nav::default(),
|
||||
profiles: vec!["billing".to_string()],
|
||||
draft,
|
||||
status: None,
|
||||
error: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// `render` turns a template failure into an error string rather than
|
||||
/// panicking, so the markup has to be asserted on.
|
||||
#[test]
|
||||
fn the_builder_carries_the_whole_draft_and_the_preview() {
|
||||
let html = render_builder(&page());
|
||||
|
||||
assert!(!html.contains("Template error"), "{html}");
|
||||
// Every part of the draft travels with the next request.
|
||||
assert!(html.contains(r#"name="column_names" value="number""#));
|
||||
assert!(html.contains(r#"name="column_indexed" value="yes""#));
|
||||
assert!(html.contains(r#"name="link_tables" value="customer""#));
|
||||
assert!(html.contains(r#"name="link_modes" value="optional""#));
|
||||
assert!(html.contains(r#"name="row_display_columns" value="number""#));
|
||||
// The preview shows the schema as it will exist.
|
||||
assert!(html.contains("customer_id"));
|
||||
assert!(html.contains("BIGSERIAL"));
|
||||
assert!(html.contains("TIMESTAMPTZ"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_page_offers_the_new_profile_option_and_the_currency_list() {
|
||||
let html = render_page(&page());
|
||||
|
||||
assert!(!html.contains("Template error"), "{html}");
|
||||
assert!(html.contains(r#"value="__new__""#));
|
||||
assert!(html.contains(r#"<datalist id="currency-codes">"#));
|
||||
assert!(html.contains(r#"<option value="EUR">"#));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn conditional_fields_follow_the_pending_column_type() {
|
||||
let mut state = page();
|
||||
assert!(!render_builder(&state).contains(r#"name="temporal_type_input""#));
|
||||
|
||||
state.draft.column_type_input = "temporal".to_string();
|
||||
let html = render_builder(&state);
|
||||
assert!(html.contains(r#"name="temporal_type_input""#));
|
||||
assert!(!html.contains(r#"name="gtin_type_input""#));
|
||||
|
||||
state.draft.column_type_input = "gtin".to_string();
|
||||
assert!(render_builder(&state).contains(r#"name="gtin_type_input""#));
|
||||
|
||||
// Money reveals rounding, and the base currency becomes editable.
|
||||
state.draft.column_type_input = "money".to_string();
|
||||
let html = render_builder(&state);
|
||||
assert!(html.contains(r#"name="column_rounding_input""#));
|
||||
assert!(html.contains(r#"list="currency-codes""#));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_new_profile_fields_appear_only_for_a_new_profile() {
|
||||
let mut state = page();
|
||||
assert!(!render_builder(&state).contains(r#"name="profile_name_input" value"#));
|
||||
|
||||
state.draft.creating_new_profile = true;
|
||||
let html = render_builder(&state);
|
||||
assert!(html.contains(r#"name="profile_name_input""#));
|
||||
assert!(html.contains(r#"name="accounting_currency" value="EUR" list="currency-codes""#));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_link_mode_is_shown_on_the_button_that_cycles_it() {
|
||||
let html = render_builder(&page());
|
||||
|
||||
assert!(html.contains(r#""action": "cycle-link", "index": "0""#));
|
||||
assert!(html.contains("mode-optional"));
|
||||
assert_eq!(LinkMode::Optional.label(), "optional");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user