web crate put add table
This commit is contained in:
@@ -16,6 +16,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
||||
|
||||
### Changed
|
||||
|
||||
- **Editable empty-table definitions** — the table-definition column page now
|
||||
calls `PutTableDefinition` to atomically remove selected columns and append
|
||||
replacements, with stable column identities and optimistic revision checks.
|
||||
Add-only changes continue to work for populated tables.
|
||||
- **The shared profile is named by the backend** — `ProfileTreeResponse`
|
||||
gained `shared_profile_name`, and every page that browses the shared tables
|
||||
now reads the name from there instead of spelling `__global` itself. Catalog
|
||||
|
||||
@@ -135,7 +135,7 @@ admin-no-profile-tables = Tento profil nemá vlastní tabulky.
|
||||
admin-no-dependencies = Bez závislostí
|
||||
admin-depends-on-prefix = Závisí na
|
||||
admin-row-display-prefix = zobrazení:
|
||||
admin-add-columns = Přidat sloupce
|
||||
admin-add-columns = Upravit sloupce
|
||||
admin-column-presentation = Aliasy sloupců
|
||||
admin-delete-table = Smazat tabulku
|
||||
admin-who-may-use-it = Kdo ji může používat
|
||||
@@ -161,9 +161,17 @@ admin-system-columns-label = Systémové sloupce · skryté před uživateli
|
||||
|
||||
# --- Stránky definice tabulky (přidat sloupce / zobrazení / smazat / kopírovat / šablona / historie)
|
||||
td-add-columns-title = Přidat sloupce
|
||||
td-adjust-columns-title = Upravit sloupce
|
||||
td-presentation-title = Zobrazení sloupců
|
||||
td-add-columns-to = Přidat sloupce do
|
||||
td-append-hint = Sloupce se přidávají na konec. Nic, co už existuje, se nemění a nové sloupce lze indexovat hned při přidání.
|
||||
td-adjust-columns-of = Upravit sloupce tabulky
|
||||
td-adjust-hint = Přidejte nové sloupce nebo označte existující k odebrání. Samotné přidání funguje i v tabulce s daty; odebrání nebo nahrazení vyžaduje prázdnou tabulku a uloží se atomicky.
|
||||
td-existing-columns = Existující sloupce
|
||||
td-new-columns = Nové sloupce k přidání
|
||||
td-remove-column = Odebrat
|
||||
td-remove-column-aria = Odebrat { $column }
|
||||
td-save-definition = Uložit definici tabulky
|
||||
td-rename-column = Aliasy sloupců
|
||||
td-rename-hint = Přejmenovává se jen zobrazený název, ne fyzický sloupec pod ním, takže uložená data a skripty zůstávají nedotčené.
|
||||
td-column-label = Sloupec
|
||||
@@ -189,7 +197,7 @@ td-col-target-column = Cílový sloupec
|
||||
td-col-description = Popis
|
||||
td-source = zdroj
|
||||
|
||||
td-could-not-add-column = Sloupec se nepodařilo přidat
|
||||
td-could-not-add-column = Sloupce se nepodařilo upravit
|
||||
td-staged = Připraveno
|
||||
td-link-alias = Alias odkazu
|
||||
td-column-name = Název sloupce
|
||||
@@ -250,6 +258,8 @@ td-global-label = Globální — všechny profily
|
||||
td-tab-aria = akce s tabulkou
|
||||
td-tab-add-columns = Přidat sloupce
|
||||
td-tab-add-columns-sub = Připojit nové sloupce
|
||||
td-tab-adjust-columns = Upravit sloupce
|
||||
td-tab-adjust-columns-sub = Přidat, odebrat nebo nahradit
|
||||
td-tab-presentation = Zobrazení
|
||||
td-tab-presentation-sub = Změnit aliasy a pořadí
|
||||
td-tab-delete = Smazat
|
||||
@@ -306,6 +316,7 @@ td-columns-added = { $count ->
|
||||
[few] { $count } sloupce přidány do `{ $table }`.
|
||||
*[other] { $count } sloupců přidáno do `{ $table }`.
|
||||
}
|
||||
td-definition-adjusted = Tabulka `{ $table }` upravena: přidáno { $added }, odebráno { $removed }.
|
||||
td-profile-copied = { $message } — zkopírováno { $tables ->
|
||||
[one] { $tables } tabulka
|
||||
[few] { $tables } tabulky
|
||||
@@ -324,7 +335,9 @@ td-err-select-table-first = Nejprve vyberte tabulku.
|
||||
td-err-profile-gone = Profil `{ $missing }` už neexistuje.
|
||||
td-err-not-a-table = `{ $missing }` není tabulka rozsahu { $scope }.
|
||||
td-err-describe-column = Před přidáním popište alespoň jeden sloupec.
|
||||
td-err-describe-change = Označte sloupec k odebrání nebo připravte alespoň jeden sloupec k přidání.
|
||||
td-err-backend-no-columns = Backend nepřidal sloupce.
|
||||
td-err-backend-no-adjustment = Backend neupravil definici tabulky.
|
||||
td-err-choose-rename = Zadejte nový název sloupce.
|
||||
td-err-unknown-column = Tento sloupec už do této tabulky nepatří. Panel níže ukazuje tabulku, jaká je teď.
|
||||
td-err-backend-no-rename = Backend nepřejmenoval sloupec.
|
||||
|
||||
@@ -139,7 +139,7 @@ admin-no-profile-tables = This profile has no tables of its own.
|
||||
admin-no-dependencies = No dependencies
|
||||
admin-depends-on-prefix = Depends on
|
||||
admin-row-display-prefix = display:
|
||||
admin-add-columns = Add columns
|
||||
admin-add-columns = Adjust columns
|
||||
admin-column-presentation = Column aliases
|
||||
admin-delete-table = Delete table
|
||||
admin-who-may-use-it = Who may use it
|
||||
@@ -163,9 +163,17 @@ admin-system-columns-label = System columns · hidden from users
|
||||
|
||||
# --- Table definition pages (add columns / presentation / delete / copy / template / history) -
|
||||
td-add-columns-title = Add columns
|
||||
td-adjust-columns-title = Adjust columns
|
||||
td-presentation-title = Column presentation
|
||||
td-add-columns-to = Add columns to
|
||||
td-append-hint = Columns are appended. Nothing that already exists is changed, and the new columns can be indexed as they are added.
|
||||
td-adjust-columns-of = Adjust columns of
|
||||
td-adjust-hint = Add new columns, or select existing columns to remove. Add-only changes also work on populated tables; removing or replacing columns requires an empty table and is saved atomically.
|
||||
td-existing-columns = Existing columns
|
||||
td-new-columns = New columns to add
|
||||
td-remove-column = Remove
|
||||
td-remove-column-aria = Remove { $column }
|
||||
td-save-definition = Save table definition
|
||||
td-rename-column = Column aliases
|
||||
td-rename-hint = Renames what the column is called, not the physical column underneath, so stored data and scripts are untouched.
|
||||
td-column-label = Column
|
||||
@@ -191,7 +199,7 @@ td-col-target-column = Target column
|
||||
td-col-description = Description
|
||||
td-source = source
|
||||
|
||||
td-could-not-add-column = Could not add the column
|
||||
td-could-not-add-column = Could not adjust the columns
|
||||
td-staged = Staged
|
||||
td-link-alias = Link alias
|
||||
td-column-name = Column name
|
||||
@@ -250,6 +258,8 @@ td-global-label = Global — all profiles
|
||||
td-tab-aria = table actions
|
||||
td-tab-add-columns = Add columns
|
||||
td-tab-add-columns-sub = Append new columns
|
||||
td-tab-adjust-columns = Adjust columns
|
||||
td-tab-adjust-columns-sub = Add, remove, or replace columns
|
||||
td-tab-presentation = Presentation
|
||||
td-tab-presentation-sub = Change aliases and order
|
||||
td-tab-delete = Delete
|
||||
@@ -305,6 +315,7 @@ td-columns-added = { $count ->
|
||||
[one] { $count } column added to `{ $table }`.
|
||||
*[other] { $count } columns added to `{ $table }`.
|
||||
}
|
||||
td-definition-adjusted = `{ $table }` adjusted: { $added } added, { $removed } removed.
|
||||
td-profile-copied = { $message } — { $tables ->
|
||||
[one] { $tables } table
|
||||
*[other] { $tables } tables
|
||||
@@ -320,7 +331,9 @@ td-err-select-table-first = Select a table first.
|
||||
td-err-profile-gone = Profile `{ $missing }` no longer exists.
|
||||
td-err-not-a-table = `{ $missing }` is not a table of { $scope }.
|
||||
td-err-describe-column = Describe at least one column before adding.
|
||||
td-err-describe-change = Select a column to remove or stage at least one column to add.
|
||||
td-err-backend-no-columns = The backend did not add the columns.
|
||||
td-err-backend-no-adjustment = The backend did not adjust the table definition.
|
||||
td-err-choose-rename = Type a new name for the column.
|
||||
td-err-unknown-column = That column is not part of this table any more. The panel below is the table as it now is.
|
||||
td-err-backend-no-rename = The backend did not rename the column.
|
||||
|
||||
@@ -135,7 +135,7 @@ admin-no-profile-tables = Tento profil nemá vlastné tabuľky.
|
||||
admin-no-dependencies = Bez závislostí
|
||||
admin-depends-on-prefix = Závisí od
|
||||
admin-row-display-prefix = zobrazenie:
|
||||
admin-add-columns = Pridať stĺpce
|
||||
admin-add-columns = Upraviť stĺpce
|
||||
admin-column-presentation = Aliasy stĺpcov
|
||||
admin-delete-table = Vymazať tabuľku
|
||||
admin-who-may-use-it = Kto ju môže používať
|
||||
@@ -161,9 +161,17 @@ admin-system-columns-label = Systémové stĺpce · skryté pred používateľmi
|
||||
|
||||
# --- Stránky definície tabuľky (pridať stĺpce / zobrazenie / vymazať / kopírovať / šablóna / história)
|
||||
td-add-columns-title = Pridať stĺpce
|
||||
td-adjust-columns-title = Upraviť stĺpce
|
||||
td-presentation-title = Zobrazenie stĺpcov
|
||||
td-add-columns-to = Pridať stĺpce do
|
||||
td-append-hint = Stĺpce sa pridávajú na koniec. Nič, čo už existuje, sa nemení a nové stĺpce možno indexovať hneď pri pridaní.
|
||||
td-adjust-columns-of = Upraviť stĺpce tabuľky
|
||||
td-adjust-hint = Pridajte nové stĺpce alebo označte existujúce na odstránenie. Samotné pridanie funguje aj v tabuľke s údajmi; odstránenie alebo nahradenie vyžaduje prázdnu tabuľku a uloží sa atómovo.
|
||||
td-existing-columns = Existujúce stĺpce
|
||||
td-new-columns = Nové stĺpce na pridanie
|
||||
td-remove-column = Odstrániť
|
||||
td-remove-column-aria = Odstrániť { $column }
|
||||
td-save-definition = Uložiť definíciu tabuľky
|
||||
td-rename-column = Aliasy stĺpcov
|
||||
td-rename-hint = Premenúva sa len zobrazený názov, nie fyzický stĺpec pod ním, takže uložené údaje a skripty zostávajú nedotknuté.
|
||||
td-column-label = Stĺpec
|
||||
@@ -189,7 +197,7 @@ td-col-target-column = Cieľový stĺpec
|
||||
td-col-description = Popis
|
||||
td-source = zdroj
|
||||
|
||||
td-could-not-add-column = Stĺpec sa nepodarilo pridať
|
||||
td-could-not-add-column = Stĺpce sa nepodarilo upraviť
|
||||
td-staged = Pripravené
|
||||
td-link-alias = Alias odkazu
|
||||
td-column-name = Názov stĺpca
|
||||
@@ -250,6 +258,8 @@ td-global-label = Globálne — všetky profily
|
||||
td-tab-aria = akcie s tabuľkou
|
||||
td-tab-add-columns = Pridať stĺpce
|
||||
td-tab-add-columns-sub = Pripojiť nové stĺpce
|
||||
td-tab-adjust-columns = Upraviť stĺpce
|
||||
td-tab-adjust-columns-sub = Pridať, odstrániť alebo nahradiť
|
||||
td-tab-presentation = Zobrazenie
|
||||
td-tab-presentation-sub = Zmeniť aliasy a poradie
|
||||
td-tab-delete = Vymazať
|
||||
@@ -306,6 +316,7 @@ td-columns-added = { $count ->
|
||||
[few] { $count } stĺpce pridané do `{ $table }`.
|
||||
*[other] { $count } stĺpcov pridaných do `{ $table }`.
|
||||
}
|
||||
td-definition-adjusted = Tabuľka `{ $table }` upravená: pridané { $added }, odstránené { $removed }.
|
||||
td-profile-copied = { $message } — skopírované { $tables ->
|
||||
[one] { $tables } tabuľka
|
||||
[few] { $tables } tabuľky
|
||||
@@ -324,7 +335,9 @@ td-err-select-table-first = Najprv vyberte tabuľku.
|
||||
td-err-profile-gone = Profil `{ $missing }` už neexistuje.
|
||||
td-err-not-a-table = `{ $missing }` nie je tabuľka rozsahu { $scope }.
|
||||
td-err-describe-column = Pred pridaním popíšte aspoň jeden stĺpec.
|
||||
td-err-describe-change = Označte stĺpec na odstránenie alebo pripravte aspoň jeden stĺpec na pridanie.
|
||||
td-err-backend-no-columns = Backend nepridal stĺpce.
|
||||
td-err-backend-no-adjustment = Backend neupravil definíciu tabuľky.
|
||||
td-err-choose-rename = Zadajte nový názov stĺpca.
|
||||
td-err-unknown-column = Tento stĺpec už do tejto tabuľky nepatrí. Panel nižšie ukazuje tabuľku, aká je teraz.
|
||||
td-err-backend-no-rename = Backend nepremenoval stĺpec.
|
||||
|
||||
@@ -289,6 +289,7 @@ pub(crate) async fn load_page(
|
||||
history,
|
||||
selection: inputs.selection,
|
||||
columns: inputs.columns,
|
||||
remove_column_ids: inputs.remove_column_ids,
|
||||
copy: inputs.copy,
|
||||
invoice: inputs.invoice,
|
||||
status: inputs.status,
|
||||
|
||||
@@ -24,6 +24,7 @@ use crate::{
|
||||
definitions::table_definition::{
|
||||
AddTableColumnsRequest, CopyProfileRequest, CreateInvoiceTemplateTableRequest,
|
||||
ColumnPresentation, DeleteTableRequest, SetColumnPresentationRequest,
|
||||
PutTableDefinitionRequest,
|
||||
},
|
||||
{i18n::Locale, tr},
|
||||
schema::{ColumnForm, proto_columns},
|
||||
@@ -214,7 +215,8 @@ pub(crate) async fn update_columns(
|
||||
}
|
||||
}
|
||||
|
||||
/// POST /admin/tables/columns/add — AddTableColumns.
|
||||
/// POST /admin/tables/columns/add — append columns, or atomically replace part
|
||||
/// of an empty table through PutTableDefinition.
|
||||
pub(crate) async fn add_columns(
|
||||
State(state): State<AppState>,
|
||||
headers: HeaderMap,
|
||||
@@ -235,6 +237,7 @@ pub(crate) async fn add_columns(
|
||||
|
||||
let mut inputs = PageInputs::for_selection(selection);
|
||||
inputs.columns = form.to_draft(catalog.clone(), false);
|
||||
inputs.remove_column_ids = form.remove_column_ids.clone();
|
||||
|
||||
if !inputs.selection.has_table() {
|
||||
let message = tr!(
|
||||
@@ -250,10 +253,10 @@ pub(crate) async fn add_columns(
|
||||
)
|
||||
.await;
|
||||
}
|
||||
if inputs.columns.is_empty() {
|
||||
if inputs.columns.is_empty() && inputs.remove_column_ids.is_empty() {
|
||||
let message = tr!(
|
||||
Locale::from_headers(&headers),
|
||||
"td-err-describe-column"
|
||||
"td-err-describe-change"
|
||||
);
|
||||
return refuse(
|
||||
state,
|
||||
@@ -266,10 +269,88 @@ pub(crate) async fn add_columns(
|
||||
}
|
||||
// The same checks the server runs, applied to a draft that may have been
|
||||
// rebuilt from a posted form rather than through the panel.
|
||||
if let Err(message) = inputs.columns.validate(Locale::from_headers(&headers)) {
|
||||
if !inputs.columns.is_empty()
|
||||
&& let Err(message) = inputs.columns.validate(Locale::from_headers(&headers))
|
||||
{
|
||||
return refuse(state, headers, inputs, Page::AddColumns, message).await;
|
||||
}
|
||||
|
||||
// Keep the old append capability for populated tables. Put is used only
|
||||
// when a removal was requested, because that operation deliberately
|
||||
// refuses every table which has ever stored a row.
|
||||
if inputs.remove_column_ids.is_empty() {
|
||||
return append_columns(state, headers, inputs, catalog, definitions).await;
|
||||
}
|
||||
|
||||
let current = match load_page(state.clone(), &headers, inputs.clone()).await {
|
||||
Ok(page) => page.detail,
|
||||
Err(error) => return load_error_response(&headers, error),
|
||||
};
|
||||
let Some(current) = current else {
|
||||
let message = tr!(Locale::from_headers(&headers), "td-err-select-table-first");
|
||||
return refuse(state, headers, inputs, Page::AddColumns, message).await;
|
||||
};
|
||||
let remove_column_ids = match current.expanded_removal_ids(&inputs.remove_column_ids) {
|
||||
Ok(ids) => ids,
|
||||
Err(()) => {
|
||||
let message = tr!(Locale::from_headers(&headers), "td-err-unknown-column");
|
||||
return refuse(state, headers, inputs, Page::AddColumns, message).await;
|
||||
}
|
||||
};
|
||||
|
||||
let request = PutTableDefinitionRequest {
|
||||
profile_name: inputs.selection.profile.clone(),
|
||||
table_name: inputs.selection.table.clone(),
|
||||
remove_column_ids,
|
||||
add_columns: proto_columns(&inputs.columns.added),
|
||||
add_indexes: inputs.columns.selected_index_names(),
|
||||
generated_aliases: Vec::new(),
|
||||
expected_row_version: form.expected_row_version,
|
||||
};
|
||||
let Ok(request) = authenticated_request(&headers, request) else {
|
||||
return Redirect::to("/login").into_response();
|
||||
};
|
||||
|
||||
match definitions.put_table_definition(request).await {
|
||||
Ok(response) if response.get_ref().success => {
|
||||
let response = response.into_inner();
|
||||
let removed = response.removed_column_ids.len();
|
||||
let added = inputs.columns.added.len();
|
||||
inputs.sql = Some(response.sql);
|
||||
inputs.status = Some(tr!(
|
||||
Locale::from_headers(&headers),
|
||||
"td-definition-adjusted",
|
||||
"added" => added as i64,
|
||||
"removed" => removed as i64,
|
||||
"table" => inputs.selection.table.clone(),
|
||||
));
|
||||
inputs.columns = crate::schema::ColumnDraft::for_append(catalog);
|
||||
inputs.remove_column_ids.clear();
|
||||
respond(state, headers, inputs, Page::AddColumns, StatusCode::OK).await
|
||||
}
|
||||
Ok(response) => {
|
||||
let message = response.into_inner().sql;
|
||||
let message = if message.is_empty() {
|
||||
tr!(Locale::from_headers(&headers), "td-err-backend-no-adjustment")
|
||||
} else {
|
||||
message
|
||||
};
|
||||
refuse(state, headers, inputs, Page::AddColumns, message).await
|
||||
}
|
||||
Err(error) => {
|
||||
refuse(state, headers, inputs, Page::AddColumns, error.message().to_string()).await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn append_columns(
|
||||
state: AppState,
|
||||
headers: HeaderMap,
|
||||
mut inputs: PageInputs,
|
||||
catalog: crate::schema::ColumnCatalog,
|
||||
mut definitions: crate::definitions::table_definition::table_definition_client::TableDefinitionClient<tonic::transport::Channel>,
|
||||
) -> Response {
|
||||
|
||||
let request = AddTableColumnsRequest {
|
||||
profile_name: inputs.selection.profile.clone(),
|
||||
table_name: inputs.selection.table.clone(),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! What can be done to a table once it exists, as one page per decision.
|
||||
//!
|
||||
//! Adding columns, presenting columns, dropping the table, copying its profile,
|
||||
//! Adjusting columns, presenting columns, dropping the table, copying its profile,
|
||||
//! generating tables from a template and reading the rename history are six
|
||||
//! different jobs, and they used to be panels stacked on one `/admin/table-definition`
|
||||
//! workspace — which meant that after creating a table you landed on a screen
|
||||
|
||||
@@ -101,6 +101,46 @@ impl TableDetailView {
|
||||
.filter(|column| column.renameable)
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Expands a top-level removal choice to the generated columns the backend
|
||||
/// requires to travel with it. PHONE/IBAN companions name their source;
|
||||
/// linked projections use `link.source`.
|
||||
pub(crate) fn expanded_removal_ids(&self, selected: &[i64]) -> Result<Vec<i64>, ()> {
|
||||
let selected = selected.iter().copied().collect::<std::collections::HashSet<_>>();
|
||||
if selected
|
||||
.iter()
|
||||
.any(|id| !self.columns.iter().any(|column| column.column_id == *id))
|
||||
{
|
||||
return Err(());
|
||||
}
|
||||
|
||||
let mut roots = selected.clone();
|
||||
for column in self.columns.iter().filter(|column| selected.contains(&column.column_id)) {
|
||||
if let Some(root_name) = column.generated_from.split('.').next()
|
||||
&& !root_name.is_empty()
|
||||
&& let Some(root) = self.columns.iter().find(|candidate| candidate.name == root_name)
|
||||
{
|
||||
roots.insert(root.column_id);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(self
|
||||
.columns
|
||||
.iter()
|
||||
.filter(|column| {
|
||||
roots.contains(&column.column_id)
|
||||
|| self.columns.iter().any(|root| {
|
||||
roots.contains(&root.column_id)
|
||||
&& (column.generated_from == root.name
|
||||
|| column
|
||||
.generated_from
|
||||
.strip_prefix(&root.name)
|
||||
.is_some_and(|suffix| suffix.starts_with('.')))
|
||||
})
|
||||
})
|
||||
.map(|column| column.column_id)
|
||||
.collect())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@@ -121,6 +161,12 @@ pub(crate) struct DetailColumn {
|
||||
}
|
||||
|
||||
impl DetailColumn {
|
||||
/// Generated/read-only rows are shown as part of their parent definition;
|
||||
/// choosing the parent expands to them in `expanded_removal_ids`.
|
||||
pub(crate) fn is_removal_choice(&self) -> bool {
|
||||
self.column_id > 0 && !self.generated && !self.read_only
|
||||
}
|
||||
|
||||
/// The badge list rendered under each column name.
|
||||
pub(crate) fn flags(&self, locale: &crate::i18n::Locale) -> Vec<String> {
|
||||
let mut flags = Vec::new();
|
||||
@@ -274,8 +320,10 @@ pub(crate) struct DeleteForm {
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub(crate) struct PageInputs {
|
||||
pub selection: Selection,
|
||||
/// The columns staged for `AddTableColumns`.
|
||||
/// The new columns staged for the structural edit.
|
||||
pub columns: ColumnDraft,
|
||||
/// Existing column identities selected for removal.
|
||||
pub remove_column_ids: Vec<i64>,
|
||||
pub copy: CopyForm,
|
||||
pub invoice: InvoiceTemplateForm,
|
||||
pub status: Option<String>,
|
||||
@@ -316,6 +364,7 @@ pub(crate) struct TableDefinitionPageState {
|
||||
pub detail: Option<TableDetailView>,
|
||||
pub history: Vec<RenameEntry>,
|
||||
pub columns: ColumnDraft,
|
||||
pub remove_column_ids: Vec<i64>,
|
||||
pub copy: CopyForm,
|
||||
pub invoice: InvoiceTemplateForm,
|
||||
pub status: Option<String>,
|
||||
@@ -328,6 +377,10 @@ pub(crate) struct TableDefinitionPageState {
|
||||
}
|
||||
|
||||
impl TableDefinitionPageState {
|
||||
pub(crate) fn column_is_selected_for_removal(&self, column_id: &i64) -> bool {
|
||||
self.remove_column_ids.contains(column_id)
|
||||
}
|
||||
|
||||
/// Whether `name` is the page being rendered. Read by context.html.
|
||||
pub(crate) fn is(&self, name: &str) -> bool {
|
||||
self.active == name
|
||||
@@ -490,5 +543,11 @@ mod tests {
|
||||
detail.columns[2].flags(&crate::i18n::Locale::default()),
|
||||
vec!["EUR", "generated from accounting"]
|
||||
);
|
||||
|
||||
assert_eq!(detail.expanded_removal_ids(&[1]).unwrap(), [1, 2]);
|
||||
assert_eq!(detail.expanded_removal_ids(&[2]).unwrap(), [1, 2]);
|
||||
assert!(detail.expanded_removal_ids(&[99]).is_err());
|
||||
assert!(detail.columns[0].is_removal_choice());
|
||||
assert!(!detail.columns[1].is_removal_choice());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -291,6 +291,7 @@ mod tests {
|
||||
}),
|
||||
history: Vec::new(),
|
||||
columns: ColumnDraft::for_append(crate::schema::tests::catalog()),
|
||||
remove_column_ids: Vec::new(),
|
||||
copy: CopyForm::default(),
|
||||
invoice: InvoiceTemplateForm::default(),
|
||||
status: None,
|
||||
@@ -453,6 +454,9 @@ mod tests {
|
||||
fn adding_columns_and_presentation_are_separate_pages() {
|
||||
let add_html = render_add_columns_page(&page());
|
||||
assert!(add_html.contains(r#"id="column-form""#), "{add_html}");
|
||||
assert!(add_html.contains(r#"name="expected_row_version" value="1""#), "{add_html}");
|
||||
assert!(add_html.contains(r#"name="remove_column_ids" value="1""#), "{add_html}");
|
||||
assert!(add_html.contains("Adjust columns"), "{add_html}");
|
||||
assert!(!add_html.contains(r#"name="alias""#), "{add_html}");
|
||||
|
||||
let presentation_html = render_presentation_page(&page());
|
||||
|
||||
@@ -1416,6 +1416,13 @@ pub(crate) struct ColumnForm {
|
||||
#[serde(default)]
|
||||
pub index: Option<usize>,
|
||||
|
||||
/// Existing-table structural edits carry these alongside the shared
|
||||
/// staged-column fields. They stay empty on the add-table builder.
|
||||
#[serde(default)]
|
||||
pub remove_column_ids: Vec<i64>,
|
||||
#[serde(default)]
|
||||
pub expected_row_version: i64,
|
||||
|
||||
#[serde(default)]
|
||||
pub column_name_input: String,
|
||||
#[serde(default)]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{# GET /admin/tables/columns/add — crate::pages::admin::table_definition::ui::AddColumnsPage #}
|
||||
{% extends "ui/base.html" %}
|
||||
|
||||
{% block title %}{{ nav.tr("td-add-columns-title") }}{% endblock %}
|
||||
{% block title %}{{ nav.tr("td-adjust-columns-title") }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<main>
|
||||
|
||||
@@ -3,12 +3,39 @@
|
||||
|
||||
{% if page.table_is_writable() %}
|
||||
<section class="panel">
|
||||
<h2>{{ nav.tr("td-add-columns-to") }} <code>{{ page.selection.table }}</code></h2>
|
||||
<p class="hint">{{ nav.tr("td-append-hint") }}</p>
|
||||
<h2>{{ nav.tr("td-adjust-columns-of") }} <code>{{ page.selection.table }}</code></h2>
|
||||
<p class="hint">{{ nav.tr("td-adjust-hint") }}</p>
|
||||
<form id="column-form"
|
||||
hx-post="/admin/tables/columns/add{{ page.selection.query() }}"
|
||||
hx-target="#table-panel" hx-swap="innerHTML"
|
||||
hx-disabled-elt="button[type=submit]">
|
||||
{% if let Some(detail) = page.detail %}
|
||||
<input type="hidden" name="expected_row_version" value="{{ detail.row_version }}">
|
||||
<h3>{{ nav.tr("td-existing-columns") }}</h3>
|
||||
<table class="builder-table">
|
||||
<thead><tr><th>{{ nav.tr("td-remove-column") }}</th><th>{{ nav.tr("td-col-name") }}</th><th>{{ nav.tr("td-col-type") }}</th><th>{{ nav.tr("td-col-notes") }}</th></tr></thead>
|
||||
<tbody>
|
||||
{% for column in detail.columns %}
|
||||
<tr>
|
||||
<td>
|
||||
{% if column.is_removal_choice() %}
|
||||
<input type="checkbox" name="remove_column_ids" value="{{ column.column_id }}"
|
||||
aria-label="{{ nav.tr_args("td-remove-column-aria", [("column", column.name.clone())]) }}"
|
||||
{% if page.column_is_selected_for_removal(column.column_id) %}checked{% endif %}>
|
||||
{% else %}<span class="hint">—</span>{% endif %}
|
||||
</td>
|
||||
<td><code>{{ column.name }}</code></td>
|
||||
<td>{{ column.field_type }}</td>
|
||||
<td>
|
||||
{% if column.generated %}<span class="tag">{{ nav.tr("column-flag-generated") }}</span>{% endif %}
|
||||
{% if column.read_only %}<span class="tag">{{ nav.tr("column-flag-read-only") }}</span>{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
<h3>{{ nav.tr("td-new-columns") }}</h3>
|
||||
<div id="column-panel">
|
||||
{% include "pages/admin/table_definition/column_panel.html" %}
|
||||
</div>
|
||||
|
||||
@@ -216,5 +216,5 @@
|
||||
{% endfor %}
|
||||
|
||||
<div class="form-actions">
|
||||
<button type="submit">{{ nav.tr_count("td-add-columns-button", "count", page.columns.added.len() as i64) }}</button>
|
||||
<button type="submit">{{ nav.tr("td-save-definition") }}</button>
|
||||
</div>
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
{% if page.selection.has_table() %}
|
||||
{% if page.table_is_writable() %}
|
||||
<a href="/admin/tables/columns/add{{ page.selection.query() }}" class="tab {% if page.is("add-columns") %}selected{% endif %}" {% if page.is("add-columns") %}aria-current="page"{% endif %}>
|
||||
<span>{{ nav.tr("td-tab-add-columns") }}</span><small>{{ nav.tr("td-tab-add-columns-sub") }}</small>
|
||||
<span>{{ nav.tr("td-tab-adjust-columns") }}</span><small>{{ nav.tr("td-tab-adjust-columns-sub") }}</small>
|
||||
</a>
|
||||
<a href="/admin/tables/presentation{{ page.selection.query() }}" class="tab {% if page.is("presentation") %}selected{% endif %}" {% if page.is("presentation") %}aria-current="page"{% endif %}>
|
||||
<span>{{ nav.tr("td-tab-presentation") }}</span><small>{{ nav.tr("td-tab-presentation-sub") }}</small>
|
||||
|
||||
Reference in New Issue
Block a user