Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions internal/admin/dashboard/static/dist/assets/index-BMuHCiY5.js

Large diffs are not rendered by default.

This file was deleted.

Large diffs are not rendered by default.

70 changes: 0 additions & 70 deletions internal/admin/dashboard/static/dist/assets/index-s6JXpqTB.js

This file was deleted.

4 changes: 2 additions & 2 deletions internal/admin/dashboard/static/dist/index.html

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1,297 changes: 1,296 additions & 1 deletion web/dashboard/messages/en.json

Large diffs are not rendered by default.

1,407 changes: 1,407 additions & 0 deletions web/dashboard/messages/pl.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion web/dashboard/project.inlang/settings.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "https://inlang.com/schema/project-settings",
"baseLocale": "en",
"locales": ["en"],
"locales": ["en", "pl"],
"modules": [
"https://cdn.jsdelivr.net/npm/@inlang/plugin-message-format@4.4.0/dist/index.js",
"https://cdn.jsdelivr.net/npm/@inlang/plugin-m-function-matcher@2.2.6/dist/index.js"
Expand Down
40 changes: 14 additions & 26 deletions web/dashboard/src/lib/components/molecules/LocaleSelector.svelte
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
<script>
// Locale picker. It deliberately renders nothing until a second locale is
// configured, so adding a translation is the only step needed to expose it.
// Settings locale picker. It deliberately renders nothing until a second
// locale is configured, so adding a translation is enough to expose it.
import { localeDisplayName } from "$lib/i18n/locale.js";
import * as m from "$lib/paraglide/messages.js";
import { getLocale, locales, setLocale } from "$lib/paraglide/runtime.js";

let { compact = false } = $props();

const availableLocales = locales.map((value) => ({
value,
label: localeDisplayName(value),
}));
</script>

{#if availableLocales.length > 1}
<label class="locale-selector" class:is-compact={compact}>
<span>{m.language_label()}</span>
<div class="settings-refresh-section locale-settings">
<label class="locale-selector-label" for="dashboard-locale">
{m.language_label()}
</label>
<select
id="dashboard-locale"
class="form-select settings-select"
value={getLocale()}
aria-label={m.language_label()}
onchange={(event) => setLocale(event.currentTarget.value)}
Expand All @@ -25,31 +27,17 @@
<option value={locale.value}>{locale.label}</option>
{/each}
</select>
</label>
</div>
{/if}

<style>
.locale-selector {
display: grid;
gap: 5px;
margin-bottom: 10px;
color: var(--text-muted);
font-size: 11px;
font-weight: 600;
.locale-settings {
grid-template-columns: minmax(280px, 420px);
}

.locale-selector select {
min-width: 0;
width: 100%;
padding: 6px 8px;
border: 1px solid var(--border);
border-radius: 6px;
background: var(--bg);
.locale-selector-label {
color: var(--text);
font: inherit;
}

.locale-selector.is-compact {
display: none;
font-size: 18px;
font-weight: 600;
}
</style>
2 changes: 0 additions & 2 deletions web/dashboard/src/lib/components/organisms/Sidebar.svelte
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<script>
import Icon from "$lib/components/atoms/Icon.svelte";
import GoModelLogo from "$lib/components/atoms/GoModelLogo.svelte";
import LocaleSelector from "$lib/components/molecules/LocaleSelector.svelte";
import ThemeToggle from "./ThemeToggle.svelte";
import { router } from "$lib/stores/router.svelte.js";
import { sidebar } from "$lib/stores/ui.svelte.js";
Expand Down Expand Up @@ -109,7 +108,6 @@
{/each}
</nav>
<div class="sidebar-footer">
<LocaleSelector compact={sidebar.collapsed} />
<ThemeToggle compact={sidebar.collapsed} />
{#if auth.externalLogoutURL}
<div class="external-auth-section">
Expand Down
4 changes: 2 additions & 2 deletions web/dashboard/src/lib/i18n/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,5 @@ and prefer complete phrases with named inputs so translators can reorder text.
3. Add the locale tag to `locales` in `project.inlang/settings.json`.
4. Run `npm test`, `npm run check`, and `npm run build`.

The locale selector reads this list automatically. Tests reject unused English
messages.
The locale selector in Settings reads this list automatically. Tests reject
unused English messages.
3 changes: 2 additions & 1 deletion web/dashboard/src/pages/audit-logs/AuditEntryMetadata.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Metadata badge strip under an expanded audit entry.
import { providerDisplayValue, qualifiedResolvedModelDisplay } from "$lib/utils/format.js";
import { workflowFailoverTarget } from "./audit-logic.js";
import * as m from "$lib/paraglide/messages.js";

let { entry } = $props();

Expand Down Expand Up @@ -40,7 +41,7 @@
</script>

<div class="audit-entry-metadata">
<span class="audit-entry-metadata-label">Metadata:</span>
<span class="audit-entry-metadata-label">{m.audit_metadata_label()}</span>
<div class="audit-entry-context">
{#each badges as badge (badge.key)}
<span class={["provider-badge", badge.class, { mono: badge.mono }]}
Expand Down
20 changes: 15 additions & 5 deletions web/dashboard/src/pages/audit-logs/AuditEntrySummary.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
statusCodeClass,
} from "./audit-logic.js";
import { ChevronDown, ChevronLeft, ChevronRight } from "lucide";
import * as m from "$lib/paraglide/messages.js";

// `thread` marks a session-thread head row (grouped mode):
// { count, expanded, ontoggle }. `expanded`/`onactivate` wire the
Expand Down Expand Up @@ -50,7 +51,7 @@
}

function threadDescription() {
return "Session with " + Number(thread.count || 1) + " requests";
return m.audit_session_description({ count: Number(thread.count || 1) });
}

function openConversation(event) {
Expand Down Expand Up @@ -82,7 +83,9 @@
title={threadDescription()}
aria-label={threadDescription() +
", " +
(thread.expanded ? "collapse" : "expand")}
(thread.expanded
? m.common_action_collapse()
: m.common_action_expand())}
onclick={toggleThread}
>
<Icon
Expand All @@ -96,7 +99,10 @@
class="audit-status-badge audit-request-badge {statusCodeClass(
entry.status_code,
)}"
title={"Status " + (entry.status_code || "-") + " · " + (entry.method || "-")}
title={m.audit_status({
status: entry.status_code || "-",
method: entry.method || "-",
})}
aria-label={(entry.status_code || "-") + " " + (entry.method || "-")}
>
<span class="audit-request-status" aria-hidden="true"
Expand Down Expand Up @@ -127,8 +133,12 @@
type="button"
class="audit-conversation-trigger"
class:audit-conversation-trigger-active={interactionsOpen}
title={interactionsOpen ? "Close interactions" : "Open interactions"}
aria-label={interactionsOpen ? "Close interactions" : "Open interactions"}
title={interactionsOpen
? m.audit_close_interactions()
: m.audit_open_interactions()}
aria-label={interactionsOpen
? m.audit_close_interactions()
: m.audit_open_interactions()}
aria-pressed={interactionsOpen}
onclick={openConversation}
>
Expand Down
23 changes: 12 additions & 11 deletions web/dashboard/src/pages/audit-logs/AuditFilters.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import { debounced } from "$lib/utils/debounce.js";
import { auditList } from "./auditList.svelte.js";
import { X } from "lucide";
import * as m from "$lib/paraglide/messages.js";

const onSearchInput = debounced(() => auditList.fetchAuditLog(true));
$effect(() => onSearchInput.cancel);
Expand All @@ -15,21 +16,21 @@
<div class="audit-filter-row audit-filter-row-search">
<FilterInput
id="audit-filter-search"
placeholder="Search by request ID, model, provider, path, user path, or error..."
label="Search by request ID, model, provider, path, user path, or error"
placeholder={m.audit_search_placeholder()}
label={m.audit_search_label()}
bind:value={auditList.auditSearch}
oninput={onSearchInput}
/>
</div>
<div class="audit-filter-row audit-filter-row-controls">
<select
id="audit-filter-method"
aria-label="HTTP method filter"
aria-label={m.audit_filter_method_label()}
class="usage-log-select audit-filter-select"
bind:value={auditList.auditMethod}
onchange={() => auditList.fetchAuditLog(true)}
>
<option value="">All Methods</option>
<option value="">{m.audit_filter_all_methods()}</option>
<option value="GET">GET</option>
<option value="POST">POST</option>
<option value="PUT">PUT</option>
Expand All @@ -38,12 +39,12 @@
</select>
<select
id="audit-filter-status"
aria-label="Status code filter"
aria-label={m.audit_filter_status_label()}
class="usage-log-select audit-filter-select"
bind:value={auditList.auditStatusCode}
onchange={() => auditList.fetchAuditLog(true)}
>
<option value="">All Statuses</option>
<option value="">{m.audit_filter_all_statuses()}</option>
<option value="200">200</option>
<option value="201">201</option>
<option value="400">400</option>
Expand All @@ -58,22 +59,22 @@
</select>
<select
id="audit-filter-stream"
aria-label="Streaming mode filter"
aria-label={m.audit_filter_stream_label()}
class="usage-log-select audit-filter-select"
bind:value={auditList.auditStream}
onchange={() => auditList.fetchAuditLog(true)}
>
<option value="">All Modes</option>
<option value="true">Streaming</option>
<option value="false">Non-streaming</option>
<option value="">{m.audit_filter_all_modes()}</option>
<option value="true">{m.audit_filter_streaming()}</option>
<option value="false">{m.audit_filter_non_streaming()}</option>
</select>
<button
type="button"
class="btn audit-clear-btn"
onclick={() => auditList.clearAuditFilters()}
>
<Icon icon={X} class="table-icon-svg" />
<span>Clear</span>
<span>{m.common_action_clear()}</span>
</button>
</div>
</div>
Expand Down
5 changes: 3 additions & 2 deletions web/dashboard/src/pages/audit-logs/AuditLiveStatus.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import { runtimeConfig } from "$lib/stores/runtimeConfig.svelte.js";
import { liveLogs } from "./liveLogs.svelte.js";
import { auditLivePauseMessage } from "./live-logs-logic.js";
import * as m from "$lib/paraglide/messages.js";

const pauseReason = $derived(liveLogs.auditLivePauseReason());
const live = $derived(liveLogs.liveLogsStreaming && !pauseReason);
Expand All @@ -16,11 +17,11 @@
<span class="audit-live-status" role="status">
<span class="live-dot" class:is-streaming={live} aria-hidden="true"></span>
{#if !liveLogs.liveLogsStreaming}
<span class="audit-live-status-text">Live stream connecting…</span>
<span class="audit-live-status-text">{m.audit_live_connecting()}</span>
{:else if pauseReason}
<span class="audit-live-status-text">{auditLivePauseMessage(pauseReason)}</span>
{:else}
<span class="audit-live-status-text is-live">Live</span>
<span class="audit-live-status-text is-live">{m.audit_live()}</span>
{/if}
</span>
{/if}
Expand Down
42 changes: 26 additions & 16 deletions web/dashboard/src/pages/audit-logs/AuditLogsPage.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import AuditThreadGroup from "./AuditThreadGroup.svelte";
import { auditList } from "./auditList.svelte.js";
import { liveLogs } from "./liveLogs.svelte.js";
import * as m from "$lib/paraglide/messages.js";
import {
auditRetentionHighlight,
auditRetentionPrefix,
Expand All @@ -27,10 +28,6 @@

const PAGE = "audit-logs";

const RETENTION_HELP =
"If you want to change the retention period, set LOGGING_RETENTION_DAYS (env var) or logging.retention_days (config.yaml) and restart the gateway. Default is 30 days; 0 keeps audit logs forever.";


const retentionRaw = $derived(
runtimeConfig.config && runtimeConfig.config.LOGGING_RETENTION_DAYS,
);
Expand Down Expand Up @@ -67,12 +64,12 @@
<div class="page-with-sticky-date">
<div class="page-header date-range-page-header">
<div class="inline-help-section">
<h2>Audit Logs</h2>
<h2>{m.audit_title()}</h2>
{#if auditRetentionText(retentionRaw)}
<InlineHelpSection
copyId="audit-retention-help-copy"
label="retention help"
text={RETENTION_HELP}
label={m.audit_retention_help_label()}
text={m.audit_retention_help()}
>
{#snippet title()}
<p class="audit-retention-note">
Expand All @@ -94,8 +91,9 @@

{#if runtimeConfig.loaded && !runtimeConfig.auditVisible() && !auth.needsAuth}
<div class="alert alert-warning" role="status">
Audit logging is off. Live entries are temporary and disappear after
refresh. Set <code>LOGGING_ENABLED=true</code> to persist them.
{m.audit_logging_disabled_before_config()}<code
>LOGGING_ENABLED=true</code
>{m.audit_logging_disabled_after_config()}
</div>
{/if}

Expand All @@ -106,11 +104,23 @@
<div class="audit-list-toolbar-left">
{#if auditList.auditLog.total > 0}
<p class="audit-log-summary">
Showing {auditList.auditLog.offset + 1}-{Math.min(
auditList.auditLog.offset + auditList.auditLog.limit,
auditList.auditLog.total,
)} of {auditList.auditLog.total}
{auditList.auditGroupSessions ? "sessions" : "logs"}
{auditList.auditGroupSessions
? m.audit_summary_sessions({
start: auditList.auditLog.offset + 1,
end: Math.min(
auditList.auditLog.offset + auditList.auditLog.limit,
auditList.auditLog.total,
),
total: auditList.auditLog.total,
})
: m.audit_summary_logs({
start: auditList.auditLog.offset + 1,
end: Math.min(
auditList.auditLog.offset + auditList.auditLog.limit,
auditList.auditLog.total,
),
total: auditList.auditLog.total,
})}
</p>
{/if}
<!-- View preference, not a filter: Clear leaves it alone. -->
Expand All @@ -120,15 +130,15 @@
checked={auditList.auditGroupSessions}
onchange={() => auditList.toggleAuditGroupSessions()}
/>
<span>Group by session</span>
<span>{m.audit_group_by_session()}</span>
</label>
</div>
<AuditLiveStatus />
</div>

{#if auditList.loading && auditList.auditLog.entries.length === 0}
<div class="audit-log-loading">
<Spinner size={18} label="Loading audit logs" />
<Spinner size={18} label={m.audit_loading()} />
</div>
{:else if auditList.auditLog.entries.length > 0}
<div class="audit-log-list">
Expand Down
Loading