i18n on the web - deepseek translations
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
{# GET / — crate::pages::analytics::ui::AnalyticsPage #}
|
||||
{% extends "ui/base.html" %}
|
||||
|
||||
{% block title %}Analytics{% endblock %}
|
||||
{% block title %}{{ nav.tr("analytics-title") }}{% endblock %}
|
||||
|
||||
{% block head %}
|
||||
<script src="https://cdn.jsdelivr.net/npm/echarts@6/dist/echarts.min.js"></script>
|
||||
@@ -12,52 +12,52 @@
|
||||
<main x-data="{ sql: '', copied: false }">
|
||||
<section class="heading">
|
||||
<div>
|
||||
<p class="eyebrow">Reporting</p>
|
||||
<h1>Analytics</h1>
|
||||
<p>Query the read-only analytics API and chart the result.</p>
|
||||
<p class="eyebrow">{{ nav.tr("analytics-eyebrow") }}</p>
|
||||
<h1>{{ nav.tr("analytics-title") }}</h1>
|
||||
<p>{{ nav.tr("analytics-description") }}</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="analytics">
|
||||
<aside class="panel sidebar">
|
||||
<h2>Available data</h2>
|
||||
<p class="hint">Load a profile to see the table and column aliases accepted by analytics SQL.</p>
|
||||
<h2>{{ nav.tr("analytics-available-data") }}</h2>
|
||||
<p class="hint">{{ nav.tr("analytics-load-hint") }}</p>
|
||||
<div hx-get="/api/profiles" hx-trigger="load" hx-target="#profile" hx-swap="innerHTML">
|
||||
<form class="catalog-load" hx-post="/api/catalog" hx-target="#catalog" hx-swap="innerHTML" hx-disabled-elt="button">
|
||||
<label>Profile
|
||||
<label>{{ nav.tr("analytics-profile") }}
|
||||
<select id="profile" name="profile_name" hx-post="/api/catalog" hx-trigger="change"
|
||||
hx-target="#catalog" hx-swap="innerHTML">
|
||||
<option value="">Loading profiles…</option>
|
||||
<option value="">{{ nav.tr("analytics-loading-profiles") }}</option>
|
||||
</select>
|
||||
</label>
|
||||
<button type="submit">Load schema</button>
|
||||
<button type="submit">{{ nav.tr("analytics-load-schema") }}</button>
|
||||
</form>
|
||||
</div>
|
||||
<div id="catalog" aria-live="polite"><p class="hint">No schema loaded.</p></div>
|
||||
<div id="catalog" aria-live="polite"><p class="hint">{{ nav.tr("analytics-no-schema") }}</p></div>
|
||||
</aside>
|
||||
|
||||
<section>
|
||||
<form id="query-form" class="panel query-form" hx-post="/api/query" hx-include="#profile"
|
||||
hx-target="#output" hx-swap="innerHTML" hx-disabled-elt="button">
|
||||
<div class="query-row">
|
||||
<label>Chart
|
||||
<label>{{ nav.tr("analytics-chart") }}
|
||||
<select name="chart_type">
|
||||
<option value="bar">Bar</option>
|
||||
<option value="line">Line</option>
|
||||
<option value="pie">Pie</option>
|
||||
<option value="scatter">Scatter</option>
|
||||
<option value="table">Table</option>
|
||||
<option value="bar">{{ nav.tr("analytics-chart-bar") }}</option>
|
||||
<option value="line">{{ nav.tr("analytics-chart-line") }}</option>
|
||||
<option value="pie">{{ nav.tr("analytics-chart-pie") }}</option>
|
||||
<option value="scatter">{{ nav.tr("analytics-chart-scatter") }}</option>
|
||||
<option value="table">{{ nav.tr("analytics-chart-table") }}</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>Max rows<input name="max_rows" type="number" min="1" value="1000"></label>
|
||||
<label>{{ nav.tr("analytics-max-rows") }}<input name="max_rows" type="number" min="1" value="1000"></label>
|
||||
</div>
|
||||
<label>SQL
|
||||
<label>{{ nav.tr("analytics-sql") }}
|
||||
<textarea id="sql" x-ref="sql" x-model="sql" name="sql" required spellcheck="false"
|
||||
placeholder="Load the schema, then choose a starter query or write a SELECT query"></textarea>
|
||||
placeholder="{{ nav.tr("analytics-sql-placeholder") }}"></textarea>
|
||||
</label>
|
||||
<div class="actions">
|
||||
<button type="submit">Run query</button>
|
||||
<span class="htmx-indicator hint">Running…</span>
|
||||
<button type="submit">{{ nav.tr("analytics-run-query") }}</button>
|
||||
<span class="htmx-indicator hint">{{ nav.tr("analytics-running") }}</span>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{# POST /api/catalog — crate::pages::analytics::ui::CatalogFragment #}
|
||||
{% if tables.is_empty() %}
|
||||
<p class="hint">This profile has no analytics tables.</p>
|
||||
<p class="hint">{{ nav.tr("analytics-no-tables") }}</p>
|
||||
{% else %}
|
||||
<p class="schema-summary"><strong>{{ tables.len() }}</strong> tables available</p>
|
||||
<p class="schema-summary">{{ nav.tr_count("analytics-tables-available", "count", tables.len() as i64) }}</p>
|
||||
<div class="catalog-tables">
|
||||
{% for table in tables %}
|
||||
<details class="catalog-table">
|
||||
@@ -10,7 +10,7 @@
|
||||
<code>{{ table.name }}</code>
|
||||
</summary>
|
||||
<button type="button" class="starter-query" data-sql="{{ table.starter_query }}"
|
||||
x-on:click="sql = $el.dataset.sql; $refs.sql.focus()">Use starter query</button>
|
||||
x-on:click="sql = $el.dataset.sql; $refs.sql.focus()">{{ nav.tr("analytics-use-starter") }}</button>
|
||||
<ul class="columns">
|
||||
{% for column in table.columns %}
|
||||
<li>
|
||||
@@ -22,10 +22,10 @@
|
||||
</ul>
|
||||
{% if !table.links.is_empty() %}
|
||||
<div class="links">
|
||||
<span>Links</span>
|
||||
<span>{{ nav.tr("analytics-links") }}</span>
|
||||
<ul>
|
||||
{% for link in table.links %}
|
||||
<li><code>{{ link.source_column }}</code> → <code>{{ link.linked_table }}</code>{% if link.required %} (required){% endif %}</li>
|
||||
<li><code>{{ link.source_column }}</code> → <code>{{ link.linked_table }}</code>{% if link.required %} ({{ nav.tr("analytics-required") }}){% endif %}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
@@ -34,11 +34,11 @@
|
||||
{% endfor %}
|
||||
</div>
|
||||
<details class="llm-context">
|
||||
<summary>LLM schema context</summary>
|
||||
<p class="hint">Copy this entire text and include it when asking an LLM to write a query.</p>
|
||||
<summary>{{ nav.tr("analytics-llm-context") }}</summary>
|
||||
<p class="hint">{{ nav.tr("analytics-llm-hint") }}</p>
|
||||
<textarea x-ref="llmSchema" readonly>{{ llm_context }}</textarea>
|
||||
<button type="button" class="secondary"
|
||||
x-on:click="navigator.clipboard.writeText($refs.llmSchema.value); copied = true">Copy context</button>
|
||||
<span class="hint" x-show="copied" x-cloak>Copied</span>
|
||||
x-on:click="navigator.clipboard.writeText($refs.llmSchema.value); copied = true">{{ nav.tr("analytics-copy-context") }}</button>
|
||||
<span class="hint" x-show="copied" x-cloak>{{ nav.tr("analytics-copied") }}</span>
|
||||
</details>
|
||||
{% endif %}
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
The swap target is a <select>, so even the failure case is an <option>.
|
||||
#}
|
||||
{%- if let Some(message) = error %}
|
||||
<option value="">Could not load profiles: {{ message }}</option>
|
||||
<option value="">{{ nav.tr_args("analytics-load-error", [("message", message.to_string())]) }}</option>
|
||||
{%- else %}
|
||||
<option value="">{% if profiles.is_empty() %}No profiles available{% else %}Choose a profile…{% endif %}</option>
|
||||
<option value="">{% if profiles.is_empty() %}{{ nav.tr("analytics-no-profiles") }}{% else %}{{ nav.tr("analytics-choose-profile") }}{% endif %}</option>
|
||||
{%- for profile in profiles %}
|
||||
<option value="{{ profile.name }}">{{ profile.name }} ({{ profile.table_count }} tables)</option>
|
||||
<option value="{{ profile.name }}">{{ profile.name }} ({{ nav.tr_count("admin-table-count", "count", profile.table_count as i64) }})</option>
|
||||
{%- endfor %}
|
||||
{%- endif %}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
Either a scrollable table or an ECharts container, depending on the chart
|
||||
type the form asked for. `chart_option` is JSON built in Rust.
|
||||
#}
|
||||
<p class="result-meta">{{ row_count }} rows in {{ elapsed_ms }} ms{% if truncated %} (truncated){% endif %}</p>
|
||||
<p class="result-meta">{{ meta }}</p>
|
||||
{% match chart_option %}
|
||||
{% when Some(option) %}
|
||||
<div class="chart" data-option="{{ option }}"
|
||||
|
||||
Reference in New Issue
Block a user