i18n on the web - deepseek translations

This commit is contained in:
Priec
2026-08-14 20:35:37 +02:00
parent 2dc0d94ba0
commit 8f1bfdbe0c
91 changed files with 4679 additions and 1237 deletions

View File

@@ -1,23 +1,23 @@
{# GET /admin — crate::pages::admin::admin::ui::AdminPage #}
{% extends "ui/base.html" %}
{% block title %}Admin panel{% endblock %}
{% block title %}{{ nav.tr("admin-title") }}{% endblock %}
{% block content %}
<main>
<section class="heading">
<div>
<p class="eyebrow">Workspace</p>
<h1>Admin panel</h1>
<p>Browse profiles, tables, and their physical columns. Pick a table to reach what can be done to it.</p>
<p class="eyebrow">{{ nav.tr("admin-eyebrow-workspace") }}</p>
<h1>{{ nav.tr("admin-title") }}</h1>
<p>{{ nav.tr("admin-description") }}</p>
</div>
<div class="actions">
{% if page.can_manage_tables %}<a href="/admin/tables/new">Add table</a>{% endif %}
{% if page.can_manage_scripts %}<a href="/admin/logic/new">Add logic</a>{% endif %}
{% if page.can_manage_validations %}<a href="/admin/validation/new">Add validation</a>{% endif %}
{% if page.can_manage_validations %}<a href="/admin/validation/sets/new">Add rule</a>{% endif %}
{% if page.can_export %}<a href="/admin/export">Export</a>{% endif %}
{% if page.can_ecb %}<a href="/admin/ecb">Exchange rates</a>{% endif %}
{% if page.can_manage_tables %}<a href="/admin/tables/new">{{ nav.tr("admin-add-table") }}</a>{% endif %}
{% if page.can_manage_scripts %}<a href="/admin/logic/new">{{ nav.tr("admin-add-logic") }}</a>{% endif %}
{% if page.can_manage_validations %}<a href="/admin/validation/new">{{ nav.tr("admin-add-validation") }}</a>{% endif %}
{% if page.can_manage_validations %}<a href="/admin/validation/sets/new">{{ nav.tr("admin-add-rule") }}</a>{% endif %}
{% if page.can_export %}<a href="/admin/export">{{ nav.tr("admin-export") }}</a>{% endif %}
{% if page.can_ecb %}<a href="/admin/ecb">{{ nav.tr("admin-exchange-rates") }}</a>{% endif %}
</div>
</section>
<div id="admin-workspace">{% include "pages/admin/admin/workspace.html" %}</div>

View File

@@ -5,10 +5,10 @@
#}
<div class="workspace" aria-live="polite">
<section class="pane">
<div class="pane-title"><h2>Profiles</h2><span>{{ page.profiles.len() }}</span></div>
<div class="pane-title"><h2>{{ nav.tr("admin-profiles") }}</h2><span>{{ page.profiles.len() }}</span></div>
<div class="pane-list">
{% if page.profiles.is_empty() %}
<p class="empty">No profiles available.</p>
<p class="empty">{{ nav.tr("admin-no-profiles-available") }}</p>
{% else %}
{% for profile in page.profiles %}
<form hx-get="/admin/workspace" hx-target="#admin-workspace" hx-swap="innerHTML">
@@ -16,8 +16,8 @@
type="submit" name="profile" value="{{ profile.scope }}">
<span>{{ profile.label }}</span>
<small>
{{ profile.table_count }} tables
{%- if profile.global %} · shared by every profile{% endif %}
{{ profile.table_count_label(nav.locale) }}
{%- if profile.global %} · {{ nav.tr("admin-shared-by-every-profile") }}{% endif %}
</small>
</button>
</form>
@@ -27,15 +27,15 @@
</section>
<section class="pane">
<div class="pane-title"><h2>Tables</h2><span>{{ page.tables.len() }}</span></div>
<div class="pane-title"><h2>{{ nav.tr("admin-tables-heading") }}</h2><span>{{ page.tables.len() }}</span></div>
<div class="pane-list">
{% if page.selected_profile.is_none() %}
<p class="empty">Select a profile to see its tables.</p>
<p class="empty">{{ nav.tr("admin-select-profile-first") }}</p>
{% else if page.tables.is_empty() %}
{% if page.is_global() %}
<p class="empty">There are no global tables.</p>
<p class="empty">{{ nav.tr("admin-no-global-tables") }}</p>
{% else %}
<p class="empty">This profile has no tables of its own.</p>
<p class="empty">{{ nav.tr("admin-no-profile-tables") }}</p>
{% endif %}
{% else %}
{% for table in page.tables %}
@@ -45,8 +45,8 @@
type="submit" name="table" value="{{ table.name }}">
<span>{{ table.name }}</span>
<small>
{%- if table.depends_on.is_empty() %}No dependencies{% else %}Depends on {{ table.depends_on|join(", ") }}{% endif %}
· display: {{ table.row_display_columns|join(", ") }}
{%- if table.depends_on.is_empty() %}{{ nav.tr("admin-no-dependencies") }}{% else %}{{ nav.tr("admin-depends-on-prefix") }} {{ table.depends_on|join(", ") }}{% endif %}
· {{ nav.tr("admin-row-display-prefix") }} {{ table.row_display_columns|join(", ") }}
</small>
</button>
</form>
@@ -58,27 +58,27 @@
{% if page.selected_table.as_deref() == Some(table.name.as_str()) %}
<div class="browser-actions">
{% if page.can_manage_tables && !table.is_system() %}
<a href="/admin/tables/columns?profile={{ page.selected_profile.as_deref().unwrap_or_default() }}&table={{ table.name }}">Add / rename columns</a>
<a class="danger-action" href="/admin/tables/delete?profile={{ page.selected_profile.as_deref().unwrap_or_default() }}&table={{ table.name }}">Delete table</a>
<a href="/admin/tables/columns?profile={{ page.selected_profile.as_deref().unwrap_or_default() }}&table={{ table.name }}">{{ nav.tr("admin-add-rename-columns") }}</a>
<a class="danger-action" href="/admin/tables/delete?profile={{ page.selected_profile.as_deref().unwrap_or_default() }}&table={{ table.name }}">{{ nav.tr("admin-delete-table") }}</a>
{% endif %}
<a href="/permissions/grants">Who may use it</a>
<a href="/permissions/grants">{{ nav.tr("admin-who-may-use-it") }}</a>
</div>
{% endif %}
{% endfor %}
{% if page.selected_table.is_none() %}
<p class="empty">Pick a table to add columns to it, delete it, or say who may use it.</p>
<p class="empty">{{ nav.tr("admin-pick-table-hint") }}</p>
{% endif %}
{% endif %}
</div>
{% if page.can_manage_tables && page.selected_profile.is_some() %}
<div class="browser-actions pane-footer">
{% if page.is_global() %}
<a href="/admin/tables/new?global=true">+ New global table</a>
<a href="/admin/tables/new?global=true">+ {{ nav.tr("admin-new-global-table") }}</a>
{% else %}
<a href="/admin/tables/new?profile={{ page.selected_profile.as_deref().unwrap_or_default() }}">+ New table</a>
<a href="/admin/tables/from-template?profile={{ page.selected_profile.as_deref().unwrap_or_default() }}">From template</a>
<a href="/admin/profiles/copy?profile={{ page.selected_profile.as_deref().unwrap_or_default() }}">Copy profile</a>
<a href="/admin/profiles/history?profile={{ page.selected_profile.as_deref().unwrap_or_default() }}">Rename history</a>
<a href="/admin/tables/new?profile={{ page.selected_profile.as_deref().unwrap_or_default() }}">+ {{ nav.tr("admin-new-table") }}</a>
<a href="/admin/tables/from-template?profile={{ page.selected_profile.as_deref().unwrap_or_default() }}">{{ nav.tr("admin-from-template") }}</a>
<a href="/admin/profiles/copy?profile={{ page.selected_profile.as_deref().unwrap_or_default() }}">{{ nav.tr("admin-copy-profile") }}</a>
<a href="/admin/profiles/history?profile={{ page.selected_profile.as_deref().unwrap_or_default() }}">{{ nav.tr("admin-rename-history") }}</a>
{% endif %}
</div>
{% endif %}
@@ -86,30 +86,30 @@
<section class="pane">
<div class="pane-title">
<h2>Columns</h2>
<span>{{ page.user_columns().len() }} user · {{ page.system_columns().len() }} system</span>
<h2>{{ nav.tr("admin-columns") }}</h2>
<span>{{ page.column_counts_label(nav.locale) }}</span>
</div>
<div class="pane-list">
{% if page.selected_table.is_none() %}
<p class="empty">Select a table to inspect its columns.</p>
<p class="empty">{{ nav.tr("admin-select-table-inspect") }}</p>
{% else if page.columns.is_empty() %}
<p class="empty">This table has no visible columns.</p>
<p class="empty">{{ nav.tr("admin-no-visible-columns") }}</p>
{% else %}
{% for column in page.user_columns() %}
<div class="column">
<span>{{ column.name }}</span>
<code>{{ column.data_type }}</code>
<small>{{ column.flags()|join(" · ") }}</small>
<small>{{ column.flags(nav.locale)|join(" · ") }}</small>
</div>
{% endfor %}
{% let system_columns = page.system_columns() %}
{% if !system_columns.is_empty() %}
<p class="group-label">System columns · hidden from users</p>
<p class="group-label">{{ nav.tr("admin-system-columns-label") }}</p>
{% for column in system_columns %}
<div class="column system">
<span>{{ column.name }}</span>
<code>{{ column.data_type }}</code>
<small>{{ column.flags()|join(" · ") }}</small>
<small>{{ column.flags(nav.locale)|join(" · ") }}</small>
</div>
{% endfor %}
{% endif %}

View File

@@ -1,22 +1,18 @@
{# GET /admin/ecb — crate::pages::admin::ecb::ui::EcbPage #}
{% extends "ui/base.html" %}
{% block title %}Exchange rates{% endblock %}
{% block title %}{{ nav.tr("ecb-title") }}{% endblock %}
{% block content %}
<main>
<section class="heading">
<div>
<p class="eyebrow">Exchange rates</p>
<h1>ECB reference rates</h1>
<p>
The importer fetches the ECB's published rates once a day and records every
attempt. Conversions read only what it has verified, so coverage is what decides
whether a foreign-currency amount can be posted at all.
</p>
<p class="eyebrow">{{ nav.tr("ecb-title") }}</p>
<h1>{{ nav.tr("ecb-heading") }}</h1>
<p>{{ nav.tr("ecb-description") }}</p>
</div>
<div class="actions">
<a href="/admin">← Admin panel</a>
<a href="/admin">{{ nav.tr("ui-back-to-admin-panel") }}</a>
</div>
</section>
@@ -24,23 +20,19 @@
<section class="panel">
<h2>
Import attempts <span class="count">{{ page.batches.len() }}</span>
{%- if page.failure_count() > 0 %}<span class="tag tag-bad">{{ page.failure_count() }} failed</span>{% endif -%}
{{ nav.tr("ecb-import-attempts") }} <span class="count">{{ page.batches.len() }}</span>
{%- if page.failure_count() > 0 %}<span class="tag tag-bad">{{ page.failed_count_label() }}</span>{% endif -%}
</h2>
{% if page.batches.is_empty() %}
<p class="hint">
The importer has not recorded an attempt. It runs on startup and then daily, so
an empty log means the server has not completed a run since this table was
created.
</p>
<p class="hint">{{ nav.tr("ecb-no-attempts-hint") }}</p>
{% else %}
<div class="table-scroll">
<table class="builder-table">
<thead>
<tr>
<th class="mark">#</th><th>Result</th><th>Started</th><th>Finished</th>
<th>Took</th><th>Window</th><th class="numeric">Observations</th>
<th>Verified through</th>
<th class="mark">#</th><th>{{ nav.tr("ecb-th-result") }}</th><th>{{ nav.tr("ecb-th-started") }}</th><th>{{ nav.tr("ecb-th-finished") }}</th>
<th>{{ nav.tr("ecb-th-took") }}</th><th>{{ nav.tr("ecb-th-window") }}</th><th class="numeric">{{ nav.tr("ecb-th-observations") }}</th>
<th>{{ nav.tr("ecb-th-verified-through") }}</th>
</tr>
</thead>
<tbody>
@@ -48,24 +40,24 @@
<tr{% if batch.failed() %} class="attempt-failed"{% endif %}>
<td class="mark">{{ batch.batch_id }}</td>
<td>
{%- if batch.succeeded() %}<span class="tag tag-ok">succeeded</span>
{%- else if batch.running() %}<span class="tag">running</span>
{%- else %}<span class="tag tag-bad">failed</span>{% endif -%}
{%- if batch.succeeded() %}<span class="tag tag-ok">{{ nav.tr("ecb-batch-succeeded") }}</span>
{%- else if batch.running() %}<span class="tag">{{ nav.tr("ecb-batch-running") }}</span>
{%- else %}<span class="tag tag-bad">{{ nav.tr("ecb-batch-failed") }}</span>{% endif -%}
</td>
<td class="moment">{{ batch.started() }}</td>
<td class="moment">
{%- if let Some(finished) = batch.finished() %}{{ finished }}
{%- else %}<span class="hint">still running</span>{% endif -%}
{%- else %}<span class="hint">{{ nav.tr("ecb-still-running") }}</span>{% endif -%}
</td>
<td class="numeric">
{%- if let Some(took) = batch.took() %}{{ took }}
{%- if let Some(took) = batch.took(nav.locale) %}{{ took }}
{%- else %}<span class="hint"></span>{% endif -%}
</td>
<td>
{{ batch.window() }}
<div class="hint endpoint" title="{{ batch.endpoint }}"><code>{{ batch.endpoint }}</code></div>
</td>
<td class="numeric">{{ batch.observations() }}</td>
<td class="numeric">{{ batch.observations(nav.locale) }}</td>
<td class="moment">
{%- if let Some(date) = batch.verified_through_date %}{{ date }}
{%- else %}<span class="hint"></span>{% endif -%}
@@ -81,11 +73,7 @@
</tbody>
</table>
</div>
<p class="hint">
Attempts are an audit record: they cannot be edited or deleted, and a batch that
re-fetched a day an earlier one already owned inserts nothing, which is why the
new count can be lower than the total.
</p>
<p class="hint">{{ nav.tr("ecb-audit-hint") }}</p>
{% endif %}
</section>
</main>

View File

@@ -12,7 +12,7 @@
id="ecb-status">
<h2>
{{ page.headline() }}
{% if page.import_running %}<span class="tag">import running</span>{% endif %}
{% if page.import_running %}<span class="tag">{{ nav.tr("ecb-import-running") }}</span>{% endif %}
</h2>
<p class="hint">{{ page.explanation() }}</p>
@@ -21,28 +21,24 @@
rule the backend applies -- so the backend applies it and sends both
bounds, rather than the page inviting the reader to do it in their head. #}
<div class="postable">
<p class="postable-lead">What can be posted right now</p>
<p class="postable-lead">{{ nav.tr("ecb-postable-lead") }}</p>
{% if let Some(through) = page.transactions_postable_through %}
<dl class="postable-grid">
<div>
<dt>Ordinary transactions</dt>
<dd>dated up to and including <strong>{{ through }}</strong></dd>
<dd class="hint">Converted at the rate published the business day before the transaction.</dd>
<dt>{{ nav.tr("ecb-ordinary-transactions") }}</dt>
<dd>{{ nav.tr("ecb-dated-up-to") }} <strong>{{ through }}</strong></dd>
<dd class="hint">{{ nav.tr("ecb-ordinary-rate-hint") }}</dd>
</div>
<div>
<dt>Statements &amp; decisive dates</dt>
<dd>dated up to and including <strong>{{ page.statements_postable_through.as_deref().unwrap_or("—") }}</strong></dd>
<dd class="hint">Converted at the rate published on that day, or the last business day before it.</dd>
<dt>{{ nav.tr("ecb-statements-title") }}</dt>
<dd>{{ nav.tr("ecb-dated-up-to") }} <strong>{{ page.statements_postable_through.as_deref().unwrap_or("—") }}</strong></dd>
<dd class="hint">{{ nav.tr("ecb-statements-rate-hint") }}</dd>
</div>
</dl>
<p class="hint">
A foreign-currency amount dated after its limit is refused outright until the
importer catches up — it is not converted at a stale rate.
</p>
<p class="hint">{{ nav.tr("ecb-postable-limit-hint") }}</p>
{% else %}
<p>
<strong>Nothing.</strong> No publication has ever been verified, so every
foreign-currency amount is refused whatever its date.
<strong>{{ nav.tr("ecb-nothing") }}</strong> {{ nav.tr("ecb-no-publication-hint") }}
</p>
{% endif %}
</div>
@@ -52,40 +48,40 @@
them read as trivia. #}
<div class="coverage">
<div class="coverage-point">
<span class="coverage-label">Verified through</span>
<span class="coverage-label">{{ nav.tr("ecb-verified-through") }}</span>
<span class="coverage-date">
{%- if let Some(date) = page.verified_through_date %}{{ date }}
{%- else %}never{% endif -%}
{%- else %}{{ nav.tr("ecb-never") }}{% endif -%}
</span>
</div>
<div class="coverage-gap">
{%- if page.healthy %}<span class="tag tag-ok">up to date</span>
{%- else if page.days_behind > 0 %}<span class="tag tag-bad">{{ page.days_behind }} publication day{% if page.days_behind != 1 %}s{% endif %} behind</span>
{%- else %}<span class="tag tag-bad">no coverage</span>{% endif -%}
{%- if page.healthy %}<span class="tag tag-ok">{{ nav.tr("ecb-up-to-date") }}</span>
{%- else if page.days_behind > 0 %}<span class="tag tag-bad">{{ page.behind_label() }}</span>
{%- else %}<span class="tag tag-bad">{{ nav.tr("ecb-no-coverage") }}</span>{% endif -%}
</div>
<div class="coverage-point">
<span class="coverage-label">Should reach</span>
<span class="coverage-label">{{ nav.tr("ecb-should-reach") }}</span>
<span class="coverage-date">{{ page.latest_verifiable_date }}</span>
</div>
</div>
<dl class="status-grid">
<div>
<dt>Last attempt</dt>
<dd>{% if let Some(when) = page.last_attempt() %}{{ when }}{% else %}<span class="hint">none recorded</span>{% endif %}</dd>
<dt>{{ nav.tr("ecb-last-attempt") }}</dt>
<dd>{% if let Some(when) = page.last_attempt() %}{{ when }}{% else %}<span class="hint">{{ nav.tr("ecb-none-recorded") }}</span>{% endif %}</dd>
</div>
<div>
<dt>Last success</dt>
<dd>{% if let Some(when) = page.last_success() %}{{ when }}{% else %}<span class="hint">none</span>{% endif %}</dd>
<dt>{{ nav.tr("ecb-last-success") }}</dt>
<dd>{% if let Some(when) = page.last_success() %}{{ when }}{% else %}<span class="hint">{{ nav.tr("ecb-none") }}</span>{% endif %}</dd>
</div>
<div>
<dt>Next import</dt>
<dt>{{ nav.tr("ecb-next-import") }}</dt>
<dd>{{ page.next_import() }}</dd>
</div>
<div>
<dt>Currencies on {% if let Some(date) = page.verified_through_date %}{{ date }}{% else %}the covered day{% endif %}</dt>
<dt>{% if let Some(date) = page.verified_through_date %}{{ nav.tr_args("ecb-currencies-on", [("date", date.clone())]) }}{% else %}{{ nav.tr("ecb-the-covered-day") }}{% endif %}</dt>
<dd>
{%- if page.covered_currencies.is_empty() %}<span class="hint">none</span>
{%- if page.covered_currencies.is_empty() %}<span class="hint">{{ nav.tr("ecb-none") }}</span>
{%- else %}{{ page.covered_currencies.len() }}{% endif -%}
</dd>
</div>
@@ -99,8 +95,8 @@
{% if let Some(failure) = page.last_failure() %}
<p class="hint failure-note">
Most recent failurebatch {{ failure.batch_id }}, started {{ failure.started() }}:
{% if let Some(message) = failure.error_message %}{{ message }}{% else %}no reason was recorded.{% endif %}
{{ nav.tr_args("ecb-most-recent-failure", [("batch", failure.batch_id.to_string()), ("started", failure.started())]) }}
{% if let Some(message) = failure.error_message %}{{ message }}{% else %}{{ nav.tr("ecb-no-reason-recorded") }}{% endif %}
</p>
{% endif %}
</section>

View File

@@ -17,33 +17,33 @@
{% import "ui/dialog.html" as panel_dialog %}
{% import "ui/toast.html" as panel_toast %}
{% if standalone_column_panel %}
{%- if let Some(message) = page.error %}{% call panel_alert::error("Could not add the column", message) %}{% endcall %}{% endif -%}
{%- if let Some(message) = page.status %}{% call panel_toast::success("Staged", message) %}{% endcall %}{% endif -%}
{%- if let Some(message) = page.error %}{% call panel_dialog::error("Could not add the column", message) %}{% endcall %}{% endif -%}
{%- if let Some(message) = page.error %}{% call panel_alert::error(nav.locale, nav.tr("td-could-not-add-column"), message) %}{% endcall %}{% endif -%}
{%- if let Some(message) = page.status %}{% call panel_toast::success(nav.tr("td-staged"), message) %}{% endcall %}{% endif -%}
{%- if let Some(message) = page.error %}{% call panel_dialog::error(nav.locale, nav.tr("td-could-not-add-column"), message) %}{% endcall %}{% endif -%}
{% endif %}
<div class="form-grid">
<label>{% if page.columns.show_link_target() %}Link alias{% else %}Column name{% endif %}
<label>{% if page.columns.show_link_target() %}{{ nav.tr("td-link-alias") }}{% else %}{{ nav.tr("td-column-name") }}{% endif %}
<input name="column_name_input" value="{{ page.columns.name_input }}" placeholder="issued_on">
{% if page.columns.show_link_target() %}<small>The alias is the column name used to distinguish this relationship.</small>{% endif %}
{% if page.columns.show_link_target() %}<small>{{ nav.tr("td-link-alias-hint") }}</small>{% endif %}
</label>
<label>Column type
<label>{{ nav.tr("td-column-type") }}
<select name="column_type_input"
hx-post="/admin/tables/columns/builder{{ page.selection.query() }}"
hx-trigger="change" hx-include="#column-form"
hx-target="#column-panel" hx-swap="innerHTML"
hx-vals='{"action": "refresh"}'>
<option value="">Choose a type</option>
<option value="">{{ nav.tr("td-choose-type") }}</option>
{% for column_type in column_types %}
<option value="{{ column_type }}" {% if page.columns.type_input == *column_type %}selected{% endif %}>{% if *column_type == "link" %}FKlink{% else %}{{ column_type }}{% endif %}</option>
<option value="{{ column_type }}" {% if page.columns.type_input == *column_type %}selected{% endif %}>{% if *column_type == "link" %}{{ nav.tr("td-fk-link") }}{% else %}{{ column_type }}{% endif %}</option>
{% endfor %}
</select>
</label>
{% if page.columns.show_temporal_type() %}
<label>Temporal type
<label>{{ nav.tr("td-temporal-type") }}
<select name="temporal_type_input">
<option value="">Choose a temporal type</option>
<option value="">{{ nav.tr("td-choose-temporal-type") }}</option>
{% for temporal_type in temporal_types %}
<option value="{{ temporal_type }}" {% if page.columns.temporal_type_input == *temporal_type %}selected{% endif %}>{{ temporal_type }}</option>
{% endfor %}
@@ -52,9 +52,9 @@
{% endif %}
{% if page.columns.show_gtin_type() %}
<label>GTIN type
<label>{{ nav.tr("td-gtin-type") }}
<select name="gtin_type_input">
<option value="">Choose a GTIN length</option>
<option value="">{{ nav.tr("td-choose-gtin-length") }}</option>
{% for gtin_type in gtin_types %}
<option value="{{ gtin_type }}" {% if page.columns.gtin_type_input == *gtin_type %}selected{% endif %}>GTIN-{{ gtin_type }}</option>
{% endfor %}
@@ -63,57 +63,57 @@
{% endif %}
{% if page.columns.show_decimal_arguments() %}
<label>Precision
<label>{{ nav.tr("td-precision") }}
<input name="decimal_precision_input" value="{{ page.columns.decimal_precision_input }}"
inputmode="numeric" placeholder="12">
<small>Total digits stored, at least 1.</small>
<small>{{ nav.tr("td-precision-hint") }}</small>
</label>
<label>Scale
<label>{{ nav.tr("td-scale") }}
<input name="decimal_scale_input" value="{{ page.columns.decimal_scale_input }}"
inputmode="numeric" placeholder="3">
<small>Digits after the point, no more than the precision.</small>
<small>{{ nav.tr("td-scale-hint") }}</small>
</label>
{% endif %}
{% if page.columns.show_link_target() %}
<label>Referenced table
<label>{{ nav.tr("td-referenced-table") }}
<select name="link_table_input">
<option value="">Choose a table</option>
<option value="">{{ nav.tr("td-choose-table") }}</option>
{% if !page.global_link_target_tables().is_empty() %}
<optgroup label="Global">
<optgroup label="{{ nav.tr("td-group-global") }}">
{% for table in page.global_link_target_tables() %}
<option value="{{ table }}" {% if page.columns.link_table_input == *table %}selected{% endif %}>{{ table }}</option>
{% endfor %}
</optgroup>
{% endif %}
{% if !page.user_link_target_tables().is_empty() %}
<optgroup label="User-created">
<optgroup label="{{ nav.tr("td-group-user") }}">
{% for table in page.user_link_target_tables() %}
<option value="{{ table }}" {% if page.columns.link_table_input == *table %}selected{% endif %}>{{ table }}</option>
{% endfor %}
</optgroup>
{% endif %}
{% if !page.system_link_target_tables().is_empty() %}
<optgroup label="System-created">
<optgroup label="{{ nav.tr("td-group-system") }}">
{% for table in page.system_link_target_tables() %}
<option value="{{ table }}" {% if page.columns.link_table_input == *table %}selected{% endif %}>{{ table }}</option>
{% endfor %}
</optgroup>
{% endif %}
</select>
{% if page.global_link_target_tables().is_empty() && page.user_link_target_tables().is_empty() && page.system_link_target_tables().is_empty() %}<small>No eligible tables exist in this scope.</small>{% endif %}
{% if page.global_link_target_tables().is_empty() && page.user_link_target_tables().is_empty() && page.system_link_target_tables().is_empty() %}<small>{{ nav.tr("td-no-eligible-tables") }}</small>{% endif %}
</label>
{% endif %}
{% if page.columns.show_money_options() %}
<label>Currency
<label>{{ nav.tr("td-currency") }}
<input name="column_currency_input" value="{{ page.columns.currency_input }}" list="currency-codes"
maxlength="3" placeholder="EUR">
</label>
<label>Rounding
<label>{{ nav.tr("td-rounding") }}
<select name="column_rounding_input">
<option value="none" {% if page.columns.rounding_input != "half-up" %}selected{% endif %}>none</option>
<option value="half-up" {% if page.columns.rounding_input == "half-up" %}selected{% endif %}>half-up</option>
<option value="none" {% if page.columns.rounding_input != "half-up" %}selected{% endif %}>{{ nav.tr("td-rounding-none") }}</option>
<option value="half-up" {% if page.columns.rounding_input == "half-up" %}selected{% endif %}>{{ nav.tr("td-rounding-half-up") }}</option>
</select>
</label>
{% endif %}
@@ -124,48 +124,48 @@
that asks for one on top.
#}
{% if page.columns.show_indexing() %}
<label>Indexing
<label>{{ nav.tr("td-indexing") }}
<select name="column_indexing_input">
<option value="no" {% if page.columns.indexing_input != "yes" %}selected{% endif %}>no</option>
<option value="yes" {% if page.columns.indexing_input == "yes" %}selected{% endif %}>yes</option>
<option value="no" {% if page.columns.indexing_input != "yes" %}selected{% endif %}>{{ nav.tr("common-no") }}</option>
<option value="yes" {% if page.columns.indexing_input == "yes" %}selected{% endif %}>{{ nav.tr("common-yes") }}</option>
</select>
</label>
{% else if page.columns.pending_is_auto_indexed() %}
<div class="field-note">
<span class="field-label">Indexing</span>
<p class="hint">Indexed automatically — a link is a foreign key, and the server indexes every one of them.</p>
<span class="field-label">{{ nav.tr("td-indexing") }}</span>
<p class="hint">{{ nav.tr("td-indexed-automatically") }}</p>
</div>
{% endif %}
<label>Required
<label>{{ nav.tr("td-required") }}
<select name="column_required_input">
<option value="no" {% if page.columns.required_input != "yes" %}selected{% endif %}>no</option>
<option value="yes" {% if page.columns.required_input == "yes" %}selected{% endif %}>yes</option>
<option value="no" {% if page.columns.required_input != "yes" %}selected{% endif %}>{{ nav.tr("common-no") }}</option>
<option value="yes" {% if page.columns.required_input == "yes" %}selected{% endif %}>{{ nav.tr("common-yes") }}</option>
</select>
<small>A row must carry a value for it.</small>
<small>{{ nav.tr("td-required-hint") }}</small>
</label>
{% if page.columns.show_quantity_ledger() %}
<label>Quantity ledger
<label>{{ nav.tr("td-quantity-ledger") }}
<select name="column_quantity_ledger_input">
<option value="no" {% if page.columns.quantity_ledger_input != "yes" %}selected{% endif %}>no</option>
<option value="yes" {% if page.columns.quantity_ledger_input == "yes" %}selected{% endif %}>yes</option>
<option value="no" {% if page.columns.quantity_ledger_input != "yes" %}selected{% endif %}>{{ nav.tr("common-no") }}</option>
<option value="yes" {% if page.columns.quantity_ledger_input == "yes" %}selected{% endif %}>{{ nav.tr("common-yes") }}</option>
</select>
<small>{{ page.columns.quantity_ledger_types() }} only.</small>
<small>{{ nav.tr_args("td-ql-only", [("types", page.columns.quantity_ledger_types())]) }}</small>
</label>
{% else %}
<p class="hint">A shared table keeps no books of its own, so it cannot hold a quantity-ledger column.</p>
<p class="hint">{{ nav.tr("td-shared-no-quantity-ledger") }}</p>
{% endif %}
</div>
<button type="button" class="secondary"
hx-post="/admin/tables/columns/builder{{ page.selection.query() }}"
hx-include="#column-form" hx-target="#column-panel" hx-swap="innerHTML"
hx-vals='{"action": "add-column"}'>Stage column</button>
hx-vals='{"action": "add-column"}'>{{ nav.tr("td-stage-column") }}</button>
{% if page.columns.added.is_empty() %}
<p class="hint">No columns staged yet. Describe one above and press <em>Stage column</em>.</p>
<p class="hint">{{ nav.tr("td-no-staged-columns") }} <em>{{ nav.tr("td-stage-column") }}</em>.</p>
{% else %}
<table class="builder-table">
<thead><tr><th>Name</th><th>Type</th><th>Indexed</th><th>Options</th><th></th></tr></thead>
<thead><tr><th>{{ nav.tr("td-col-name") }}</th><th>{{ nav.tr("td-col-type") }}</th><th>{{ nav.tr("td-col-indexed") }}</th><th>{{ nav.tr("td-col-options") }}</th><th></th></tr></thead>
<tbody>
{% for column in page.columns.added %}
<tr>
@@ -182,21 +182,21 @@
{% if column.indexed %}[x]{% else %}[ ]{% endif %}
</button>
{% else if column.is_indexed() %}
<span class="tag">indexed automatically</span>
<span class="tag">{{ nav.tr("td-indexed-automatically-tag") }}</span>
{% else %}
<span class="hint"></span>
{% endif %}
</td>
<td>
{% if column.required %}<span class="tag">required</span>{% endif %}
{% if column.quantity_ledger %}<span class="tag">quantity ledger</span>{% endif %}
{% if !column.currency.is_empty() %}<span class="tag">{{ column.currency }}, {{ column.money_mode.label() }}</span>{% endif %}
{% if column.required %}<span class="tag">{{ nav.tr("column-flag-required") }}</span>{% endif %}
{% if column.quantity_ledger %}<span class="tag">{{ nav.tr("column-flag-quantity-ledger") }}</span>{% endif %}
{% if !column.currency.is_empty() %}<span class="tag">{{ column.currency }}, {{ column.money_mode.display_label(nav.locale) }}</span>{% endif %}
</td>
<td>
<button type="button" class="danger"
hx-post="/admin/tables/columns/builder{{ page.selection.query() }}"
hx-include="#column-form" hx-target="#column-panel" hx-swap="innerHTML"
hx-vals='{"action": "remove-column", "index": "{{ loop.index0 }}"}'>Remove</button>
hx-vals='{"action": "remove-column", "index": "{{ loop.index0 }}"}'>{{ nav.tr("common-remove") }}</button>
</td>
</tr>
{% endfor %}
@@ -216,5 +216,5 @@
{% endfor %}
<div class="form-actions">
<button type="submit">Add {{ page.columns.added.len() }} column(s) to the table</button>
<button type="submit">{{ nav.tr_count("td-add-columns-button", "count", page.columns.added.len() as i64) }}</button>
</div>

View File

@@ -1,7 +1,7 @@
{# GET /admin/tables/columns — crate::pages::admin::table_definition::ui::ColumnsPage #}
{% extends "ui/base.html" %}
{% block title %}Columns{% endblock %}
{% block title %}{{ nav.tr("td-columns-title") }}{% endblock %}
{% block content %}
<main>

View File

@@ -12,8 +12,8 @@
Add-table builder posts, and both are decoded by the same code.
#}
<section class="panel">
<h2>Add columns to <code>{{ page.selection.table }}</code></h2>
<p class="hint">Columns are appended. Nothing that already exists is changed, and the new columns can be indexed as they are added.</p>
<h2>{{ nav.tr("td-add-columns-to") }} <code>{{ page.selection.table }}</code></h2>
<p class="hint">{{ nav.tr("td-append-hint") }}</p>
<form id="column-form"
hx-post="/admin/tables/columns{{ page.selection.query() }}"
hx-target="#table-panel" hx-swap="innerHTML"
@@ -26,27 +26,27 @@
{% if let Some(detail) = page.detail %}
<section class="panel">
<h2>Rename a column</h2>
<p class="hint">Renames what the column is called, not the physical column underneath, so stored data and scripts are untouched. Only possible while the table has no rows.</p>
<h2>{{ nav.tr("td-rename-column") }}</h2>
<p class="hint">{{ nav.tr("td-rename-hint") }}</p>
<form hx-post="/admin/tables/rename"
hx-target="#table-panel" hx-swap="innerHTML">
<input type="hidden" name="profile" value="{{ page.selection.profile }}">
<input type="hidden" name="table" value="{{ page.selection.table }}">
<div class="form-grid">
<label>Column
<label>{{ nav.tr("td-column-label") }}
<select name="old_column_name">
<option value="">Choose a column</option>
<option value="">{{ nav.tr("td-choose-column") }}</option>
{% for column in detail.renameable_columns() %}
<option value="{{ column.name }}" {% if page.rename.old_column_name == column.name %}selected{% endif %}>{{ column.name }}</option>
{% endfor %}
</select>
</label>
<label>New name
<label>{{ nav.tr("td-new-name") }}
<input name="new_column_name" value="{{ page.rename.new_column_name }}" placeholder="invoice_number">
</label>
</div>
<div class="form-actions">
<button type="submit">Rename column</button>
<button type="submit">{{ nav.tr("td-rename-button") }}</button>
</div>
</form>
</section>
@@ -55,16 +55,16 @@
{% if let Some(detail) = page.detail %}
<section class="panel">
<h2>Columns it has now <span class="count">{{ detail.columns.len() }}</span></h2>
<h2>{{ nav.tr("td-columns-now") }} <span class="count">{{ detail.columns.len() }}</span></h2>
<table class="builder-table">
<thead><tr><th>Column</th><th>Type</th><th>Notes</th></tr></thead>
<thead><tr><th>{{ nav.tr("td-col-column") }}</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><code>{{ column.name }}</code></td>
<td>{{ column.field_type }}{% if !column.sql_type.is_empty() %} <small class="hint">{{ column.sql_type }}</small>{% endif %}</td>
<td>
{%- for flag in column.flags() %}<span class="tag">{{ flag }}</span>{% endfor -%}
{%- for flag in column.flags(nav.locale) %}<span class="tag">{{ flag }}</span>{% endfor -%}
</td>
</tr>
{% endfor %}
@@ -72,9 +72,9 @@
</table>
{% if !detail.scripts.is_empty() %}
<h3 class="panel-subhead">Scripts</h3>
<h3 class="panel-subhead">{{ nav.tr("td-scripts") }}</h3>
<table class="builder-table">
<thead><tr><th>Target column</th><th>Type</th><th>Description</th></tr></thead>
<thead><tr><th>{{ nav.tr("td-col-target-column") }}</th><th>{{ nav.tr("td-col-type") }}</th><th>{{ nav.tr("td-col-description") }}</th></tr></thead>
<tbody>
{% for script in detail.scripts %}
<tr>
@@ -82,7 +82,7 @@
<td>{{ script.target_column_type }}</td>
<td>
{{ script.description }}
<details><summary>source</summary><pre class="sql-preview">{{ script.script }}</pre></details>
<details><summary>{{ nav.tr("td-source") }}</summary><pre class="sql-preview">{{ script.script }}</pre></details>
</td>
</tr>
{% endfor %}

View File

@@ -13,64 +13,60 @@
<section class="heading">
<div>
<p class="eyebrow">
{%- if page.selection.has_table() %}{{ page.selection.scope_label() }}{% else %}Table definition{% endif -%}
{%- if page.selection.has_table() %}{{ page.selection.scope_label(nav.locale) }}{% else %}{{ nav.tr("td-eyebrow-table-definition") }}{% endif -%}
</p>
<h1>
{%- if page.selection.has_table() %}{{ page.selection.table }}{% else %}{{ page.selection.scope_label() }}{% endif -%}
{%- if page.selection.has_table() %}{{ page.selection.table }}{% else %}{{ page.selection.scope_label(nav.locale) }}{% endif -%}
</h1>
<p>
{%- if let Some(detail) = page.detail -%}
{{ detail.columns.len() }} columns · identified by
{%- if detail.row_display_columns.is_empty() %} its id
{{ nav.tr_count("td-columns-count", "count", detail.columns.len() as i64) }} · {{ nav.tr("td-identified-by") }}
{%- if detail.row_display_columns.is_empty() %} {{ nav.tr("td-its-id") }}
{%- else %} {{ detail.row_display_columns|join(", ") }}{% endif -%}
{%- if let Some(summary) = page.selected_table() -%}
{%- if !summary.depends_on.is_empty() %} · depends on {{ summary.depends_on|join(", ") }}{% endif -%}
{%- if !summary.depends_on.is_empty() %} · {{ nav.tr("td-depends-on-prefix") }} {{ summary.depends_on|join(", ") }}{% endif -%}
{%- endif -%}
.
{%- else -%}
Everything on this page acts on this profile.
{{ nav.tr("td-acts-on-profile") }}
{%- endif -%}
</p>
</div>
<div class="actions">
<a href="/admin{% if page.selection.has_table() %}{{ page.selection.query() }}{% else %}{{ page.selection.profile_query() }}{% endif %}">← Admin panel</a>
<a href="/admin{% if page.selection.has_table() %}{{ page.selection.query() }}{% else %}{{ page.selection.profile_query() }}{% endif %}">{{ nav.tr("ui-back-to-admin-panel") }}</a>
</div>
</section>
<nav class="tabs" aria-label="table actions">
<nav class="tabs" aria-label="{{ nav.tr("td-tab-aria") }}">
{% if page.selection.has_table() %}
{% if page.table_is_writable() %}
<a href="/admin/tables/columns{{ page.selection.query() }}" class="tab {% if page.is("columns") %}selected{% endif %}" {% if page.is("columns") %}aria-current="page"{% endif %}>
<span>Columns</span><small>Append columns and rename them</small>
<span>{{ nav.tr("td-tab-columns") }}</span><small>{{ nav.tr("td-tab-columns-sub") }}</small>
</a>
<a href="/admin/tables/delete{{ page.selection.query() }}" class="tab {% if page.is("delete") %}selected{% endif %}" {% if page.is("delete") %}aria-current="page"{% endif %}>
<span>Delete</span><small>Drop this table</small>
<span>{{ nav.tr("td-tab-delete") }}</span><small>{{ nav.tr("td-tab-delete-sub") }}</small>
</a>
{% endif %}
<a href="/permissions/grants" class="tab">
<span>Access</span><small>Which roles may read and write it</small>
<span>{{ nav.tr("td-tab-access") }}</span><small>{{ nav.tr("td-tab-access-sub") }}</small>
</a>
{% endif %}
{% if page.selection.has_profile() && !page.selection.is_global() %}
<a href="/admin/profiles/copy{{ page.selection.profile_query() }}" class="tab {% if page.is("copy") %}selected{% endif %}" {% if page.is("copy") %}aria-current="page"{% endif %}>
<span>Copy profile</span><small>Duplicate its structure</small>
<span>{{ nav.tr("td-tab-copy") }}</span><small>{{ nav.tr("td-tab-copy-sub") }}</small>
</a>
<a href="/admin/tables/from-template{{ page.selection.profile_query() }}" class="tab {% if page.is("template") %}selected{% endif %}" {% if page.is("template") %}aria-current="page"{% endif %}>
<span>From template</span><small>Create tables from a Typst invoice</small>
<span>{{ nav.tr("td-tab-template") }}</span><small>{{ nav.tr("td-tab-template-sub") }}</small>
</a>
<a href="/admin/profiles/history{{ page.selection.profile_query() }}" class="tab {% if page.is("history") %}selected{% endif %}" {% if page.is("history") %}aria-current="page"{% endif %}>
<span>Rename history</span><small>How columns got their names</small>
<span>{{ nav.tr("td-tab-history") }}</span><small>{{ nav.tr("td-tab-history-sub") }}</small>
</a>
{% endif %}
</nav>
{% if page.selection.has_table() && !page.table_is_writable() %}
<section class="panel">
<h2>Backend-managed</h2>
<p class="hint">
This table is the backend's own. Its definition is visible in the
<a href="/admin{{ page.selection.query() }}">admin panel</a>, but it can only be
changed through the backend's own APIs, so none of the write actions apply to it.
</p>
<h2>{{ nav.tr("td-backend-managed-title") }}</h2>
<p class="hint">{{ nav.tr("td-backend-managed-hint")|safe }}</p>
</section>
{% endif %}

View File

@@ -1,7 +1,7 @@
{# GET /admin/profiles/copy — crate::pages::admin::table_definition::ui::CopyPage #}
{% extends "ui/base.html" %}
{% block title %}Copy profile{% endblock %}
{% block title %}{{ nav.tr("td-copy-title") }}{% endblock %}
{% block content %}
<main>

View File

@@ -5,13 +5,13 @@
{% include "pages/admin/table_definition/feedback.html" %}
<section class="panel">
<h2>Copy <code>{{ page.selection.profile }}</code> into a new profile</h2>
<p class="hint">Copies structure — tables, links and scripts — and no rows. Leave every table unticked to copy the whole profile.</p>
<h2>{{ nav.tr("td-copy-into") }} <code>{{ page.selection.profile }}</code></h2>
<p class="hint">{{ nav.tr("td-copy-hint") }}</p>
<form hx-post="/admin/profiles/copy"
hx-target="#table-panel" hx-swap="innerHTML">
<input type="hidden" name="profile" value="{{ page.selection.profile }}">
<div class="form-grid">
<label>New profile name
<label>{{ nav.tr("td-new-profile-name") }}
<input name="target_profile_name" value="{{ page.copy.target_profile_name }}" placeholder="billing_2027">
</label>
</div>
@@ -27,7 +27,7 @@
</div>
{% endif %}
<div class="form-actions">
<button type="submit">Copy profile</button>
<button type="submit">{{ nav.tr("td-copy-button") }}</button>
</div>
</form>
</section>

View File

@@ -1,7 +1,7 @@
{# GET /admin/tables/delete — crate::pages::admin::table_definition::ui::DeletePage #}
{% extends "ui/base.html" %}
{% block title %}Delete table{% endblock %}
{% block title %}{{ nav.tr("td-delete-title") }}{% endblock %}
{% block content %}
<main>

View File

@@ -7,14 +7,11 @@
{% if page.table_is_writable() %}
<section class="panel danger-panel">
<h2>Delete <code>{{ page.selection.table }}</code></h2>
<p class="hint">
Drops the table and its definition, and the profile too when this was its
last table. The backend refuses to delete a table that still has rows.
</p>
<h2>{{ nav.tr("td-delete-table") }} <code>{{ page.selection.table }}</code></h2>
<p class="hint">{{ nav.tr("td-delete-hint") }}</p>
{% if let Some(summary) = page.selected_table() %}
{% if !summary.depends_on.is_empty() %}
<p class="hint">It links to {{ summary.depends_on|join(", ") }}.</p>
<p class="hint">{{ nav.tr_args("td-links-to", [("tables", summary.depends_on.join(", "))]) }}</p>
{% endif %}
{% endif %}
<form hx-post="/admin/tables/delete"
@@ -22,18 +19,18 @@
<input type="hidden" name="profile" value="{{ page.selection.profile }}">
<input type="hidden" name="table" value="{{ page.selection.table }}">
<div class="form-grid">
<label class="wide">Type <code>{{ page.selection.table }}</code> to confirm
<label class="wide">{{ nav.tr("td-type-to-confirm") }} <code>{{ page.selection.table }}</code>
<input name="confirm_table_name" value="" autocomplete="off" placeholder="{{ page.selection.table }}">
</label>
</div>
<div class="form-actions">
<button type="submit" class="danger-submit">Delete table</button>
<button type="submit" class="danger-submit">{{ nav.tr("td-delete-table") }}</button>
</div>
</form>
</section>
{% else if !page.selection.has_table() %}
<section class="panel">
<h2>No table chosen</h2>
<p class="hint">Pick the table to delete in the <a href="/admin{{ page.selection.profile_query() }}">admin panel</a> first.</p>
<h2>{{ nav.tr("td-no-table-chosen") }}</h2>
<p class="hint">{{ nav.tr("td-pick-table-delete")|safe }}</p>
</section>
{% endif %}

View File

@@ -10,14 +10,14 @@
{% import "ui/dialog.html" as feedback_dialog %}
{% import "ui/toast.html" as feedback_toast %}
{%- if let Some(message) = page.error %}{% call feedback_alert::error("Could not continue", message) %}{% endcall %}{% endif -%}
{%- if let Some(message) = page.status %}{% call feedback_toast::success("Done", message) %}{% endcall %}{% endif -%}
{%- if let Some(message) = page.error %}{% call feedback_dialog::error("Could not continue", message) %}{% endcall %}{% endif -%}
{%- if let Some(message) = page.error %}{% call feedback_alert::error(nav.locale, nav.tr("td-could-not-continue"), message) %}{% endcall %}{% endif -%}
{%- if let Some(message) = page.status %}{% call feedback_toast::success(nav.tr("td-done"), message) %}{% endcall %}{% endif -%}
{%- if let Some(message) = page.error %}{% call feedback_dialog::error(nav.locale, nav.tr("td-could-not-continue"), message) %}{% endcall %}{% endif -%}
{% if let Some(sql) = page.sql %}
{% if !sql.is_empty() %}
<section class="panel">
<h2>SQL the backend ran</h2>
<h2>{{ nav.tr("td-sql-ran") }}</h2>
<pre class="sql-preview">{{ sql }}</pre>
</section>
{% endif %}

View File

@@ -1,7 +1,7 @@
{# GET /admin/tables/from-template — crate::pages::admin::table_definition::ui::TemplatePage #}
{% extends "ui/base.html" %}
{% block title %}Create from template{% endblock %}
{% block title %}{{ nav.tr("td-template-title") }}{% endblock %}
{% block content %}
<main>

View File

@@ -6,14 +6,14 @@
{% if !page.generated.is_empty() %}
<section class="panel">
<h2>Tables created from the template</h2>
<h2>{{ nav.tr("td-template-created-tables") }}</h2>
<table class="builder-table">
<thead><tr><th>Table</th><th>Collection</th><th>Parent</th></tr></thead>
<thead><tr><th>{{ nav.tr("td-col-table") }}</th><th>{{ nav.tr("td-col-collection") }}</th><th>{{ nav.tr("td-col-parent") }}</th></tr></thead>
<tbody>
{% for generated in page.generated %}
<tr>
<td><code>{{ generated.table_name }}</code></td>
<td>{% if generated.collection_path.is_empty() %}<span class="hint">root</span>{% else %}<code>{{ generated.collection_path }}</code>{% endif %}</td>
<td>{% if generated.collection_path.is_empty() %}<span class="hint">{{ nav.tr("td-root") }}</span>{% else %}<code>{{ generated.collection_path }}</code>{% endif %}</td>
<td>{% if generated.parent_table_name.is_empty() %}<span class="hint"></span>{% else %}<code>{{ generated.parent_table_name }}</code>{% endif %}</td>
</tr>
{% endfor %}
@@ -23,31 +23,26 @@
{% endif %}
<section class="panel">
<h2>Create tables from an invoice template</h2>
<p class="hint">
Reads the <code>#let komp_ac_fields = (…)</code> contract out of a Typst
template. A path that matches an existing table and column becomes a
reference to it; anything else becomes a TEXT column to refine later, and
every <code>[]</code> becomes a child table.
</p>
<h2>{{ nav.tr("td-template-create-heading") }}</h2>
<p class="hint">{{ nav.tr("td-template-hint")|safe }}</p>
<form hx-post="/admin/tables/from-template"
hx-target="#table-panel" hx-swap="innerHTML"
hx-disabled-elt="button[type=submit]">
<input type="hidden" name="profile" value="{{ page.selection.profile }}">
<div class="form-grid">
<label>Table name
<label>{{ nav.tr("td-table-name") }}
<input name="table_name" value="{{ page.invoice.table_name }}" placeholder="invoice">
</label>
<label>Row display columns
<label>{{ nav.tr("td-row-display-columns") }}
<input name="row_display_columns" value="{{ page.invoice.row_display_columns }}" placeholder="number, issued_on">
<small>Comma-separated, and only for the root table.</small>
<small>{{ nav.tr("td-row-display-hint") }}</small>
</label>
<label class="wide">Template source
<label class="wide">{{ nav.tr("td-template-source") }}
<textarea name="typst_source" rows="10" placeholder="#let komp_ac_fields = (&#10; &quot;sidlo.nazov&quot;,&#10; &quot;people[].name&quot;,&#10;)">{{ page.invoice.typst_source }}</textarea>
</label>
</div>
<div class="form-actions">
<button type="submit">Create from template</button>
<button type="submit">{{ nav.tr("td-template-create-button") }}</button>
</div>
</form>
</section>

View File

@@ -7,21 +7,23 @@
#}
{% extends "ui/base.html" %}
{% block title %}Rename history{% endblock %}
{% block title %}{{ nav.tr("td-history-title") }}{% endblock %}
{% block content %}
<main>
{% include "pages/admin/table_definition/context.html" %}
<section class="panel">
<h2>Column rename history</h2>
<h2>{{ nav.tr("td-history-heading") }}</h2>
{% if page.history.is_empty() %}
<p class="hint">
No column in {% if page.selection.has_table() %}<code>{{ page.selection.table }}</code>{% else %}this profile{% endif %} has been renamed.
</p>
{% if page.selection.has_table() %}
<p class="hint">{{ nav.tr_args("td-history-empty", [("scope", page.selection.table.clone())]) }}</p>
{% else %}
<p class="hint">{{ nav.tr("td-history-empty-this-profile") }}</p>
{% endif %}
{% else %}
<table class="builder-table">
<thead><tr><th>Table</th><th>Was</th><th>Is</th><th>When</th></tr></thead>
<thead><tr><th>{{ nav.tr("td-col-table") }}</th><th>{{ nav.tr("td-col-was") }}</th><th>{{ nav.tr("td-col-is") }}</th><th>{{ nav.tr("td-col-when") }}</th></tr></thead>
<tbody>
{% for entry in page.history %}
<tr>