fix(ui): remove tab scrollbar, relocate PROD indicator

- Remove overflow-x-auto/scrollbar-thin from translate job tabs, use flex-1 to fill width
- Remove redundant PROD badge from TopNavbar
- Move PROD context indicator into breadcrumbs row (right-aligned compact badge)
- Remove full-width warning banner and red left border from page content
This commit is contained in:
2026-06-11 16:14:14 +03:00
parent 2796ba5267
commit ffc1d39f08
3 changed files with 45 additions and 34 deletions

View File

@@ -490,13 +490,7 @@
</option>
{/each}
</select>
{#if isProdContext}
<span
class="rounded-md bg-destructive px-2 py-1 text-xs font-bold text-white"
>
PROD
</span>
{/if}
</div>
{/if}

View File

@@ -66,30 +66,17 @@
<!-- Top Navigation Bar -->
<TopNavbar />
<!-- Breadcrumbs -->
<div class="mt-16 pt-3">
<div class="mt-16 flex items-center justify-between px-4 pt-3 md:px-6">
<Breadcrumbs />
{#if isProductionContext}
<span class="flex items-center gap-1.5 rounded-full border border-warning/30 bg-warning-light px-3 py-1 text-xs font-semibold uppercase tracking-wide text-warning">
PROD
</span>
{/if}
</div>
{#if isProductionContext}
<div class="mx-4 mb-3 md:mx-6">
<div class="flex items-center gap-3 rounded-lg border border-destructive-ring bg-gradient-to-r from-red-50 to-red-100 px-4 py-3 shadow-sm ring-1 ring-destructive-ring">
<div class="flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-destructive text-white shadow-sm">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z"/></svg>
</div>
<div class="flex-1 text-sm">
<span class="font-bold text-destructive">{$t.common?.prod_context_warning || "PROD context active"}:</span>
<span class="ml-1.5 font-semibold text-destructive">{selectedEnvironment?.name || selectedEnvironment?.id || 'environment'}</span>
</div>
<div class="hidden flex-shrink-0 items-center gap-1.5 rounded-full bg-destructive/10 px-3 py-1 text-xs font-semibold uppercase tracking-wide text-destructive sm:flex">
<span class="h-1.5 w-1.5 animate-pulse rounded-full bg-destructive"></span>
PROD
</div>
</div>
</div>
{/if}
<!-- Page content -->
<div class="flex-grow px-4 pb-6 pt-2 md:px-6 {isProductionContext ? 'md:border-l-4 md:border-destructive-ring' : ''}">
<div class="flex-grow px-4 pb-6 pt-2 md:px-6">
{@render children?.()}
</div>

View File

@@ -75,8 +75,8 @@
<div class="container mx-auto px-4 py-6 max-w-full xl:max-w-7xl">
<!-- Header -->
<div class="flex items-center justify-between mb-6">
<div>
<div class="flex flex-col sm:flex-row sm:items-center justify-between gap-3 mb-6">
<div class="min-w-0 flex-1">
<h1 class="text-2xl font-bold text-text">
{m.isNewJob ? _t.translate?.config?.new_title : _t.translate?.config?.edit_title}
</h1>
@@ -84,11 +84,17 @@
<p class="text-sm text-text-muted mt-1">{_t.translate?.config?.id_label.replace('{id}', m.existingJob.id)}</p>
{/if}
</div>
<div class="flex items-center gap-3">
<button onclick={() => goto(ROUTES.translate.list())} class="px-4 py-2 border border-border-strong text-text rounded-lg hover:bg-surface-muted transition-colors">
<div class="flex items-center gap-2 sm:gap-3 shrink-0">
{#if m.isDirty}
<span class="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-medium bg-warning-light text-warning border border-warning">
<span class="w-1.5 h-1.5 rounded-full bg-warning animate-pulse"></span>
{_t.translate?.config?.unsaved_changes_title || 'Unsaved changes'}
</span>
{/if}
<button onclick={() => goto(ROUTES.translate.list())} class="px-3 sm:px-4 py-2 border border-border-strong text-text rounded-lg hover:bg-surface-muted transition-colors text-sm sm:text-base whitespace-nowrap">
{_t.translate?.config?.cancel}
</button>
<button onclick={() => m.saveJob()} disabled={m.isSaving} class="px-6 py-2 bg-primary text-white rounded-lg hover:bg-primary-hover disabled:opacity-50 disabled:cursor-not-allowed transition-colors">
<button onclick={() => m.saveJob()} disabled={m.isSaving} class="px-4 sm:px-6 py-2 bg-primary text-white rounded-lg hover:bg-primary-hover disabled:opacity-50 disabled:cursor-not-allowed transition-colors text-sm sm:text-base whitespace-nowrap">
{m.isSaving ? _t.translate?.config?.saving : _t.translate?.config?.save}
</button>
</div>
@@ -127,12 +133,12 @@
{:else if m.uxState === 'configured' || m.uxState === 'saving' || m.uxState === 'validation_error' || m.uxState === 'idle'}
<!-- Tab Navigation -->
<div class="border-b border-border mb-6">
<nav class="flex space-x-1" aria-label="Tabs">
<nav class="flex gap-1" aria-label="Tabs">
{#each m.tabs as tab}
<button
onclick={() => { m.activeTab = tab.id; }}
disabled={(tab.id === 'preview' && (!m.configValid || m.isNewJob)) || (tab.id === 'target' && m.isNewJob)}
class="px-4 py-2.5 text-sm font-medium rounded-t-lg transition-colors
class="flex-1 px-3 sm:px-4 py-2.5 text-sm font-medium rounded-t-lg transition-colors whitespace-nowrap
{m.activeTab === tab.id
? 'bg-surface-card text-primary border border-b-white border-border -mb-px'
: 'text-text-muted hover:text-text hover:bg-surface-muted'}
@@ -145,6 +151,9 @@
</nav>
</div>
<!-- Form content with dirty tracking -->
<div oninput={() => m.markDirty()} onchange={() => m.markDirty()}>
<!-- Config tab -->
<div class={m.activeTab === 'config' ? '' : 'hidden'}>
<ConfigTabForm
@@ -218,9 +227,13 @@
bind:batchSize={m.batchSize}
bind:includeSourceReference={m.includeSourceReference}
bind:upsertStrategy={m.upsertStrategy}
bind:insertMethod={m.insertMethod}
bind:connectionId={m.connectionId}
databaseDialect={m.databaseDialect}
environmentId={m.environmentId}
databases={m.databases}
databasesLoading={m.databasesLoading}
onSchemaCheckResult={(ok) => { m.schemaValidated = ok; }}
/>
{:else}
<div class="bg-surface-muted border border-border rounded-lg p-8 text-center">
@@ -232,6 +245,20 @@
<!-- Run tab -->
<div class={m.activeTab === 'run' ? '' : 'hidden'}>
{#if !m.isNewJob && m.existingJob}
<!-- Insert method badge — visible before run -->
<div class="mb-4 flex items-center gap-2">
<span class="text-xs text-text-muted">{_t.translate?.config?.insert_method || 'Insert Method'}:</span>
<span class="inline-flex items-center gap-1 px-2 py-0.5 rounded text-xs font-medium border
{m.insertMethod === 'direct_db' ? 'bg-primary/10 text-primary border-primary/20' : 'bg-surface-muted text-text-subtle border-border'}">
{#if m.insertMethod === 'direct_db'}
<svg class="w-3 h-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M4 7v10c0 2 1 3 3 3h10c2 0 3-1 3-3V7M4 7c0-2 1-3 3-3h10c2 0 3 1 3 3M4 7h16M9 11h.01M15 11h.01M9 15h.01M15 15h.01"/></svg>
{_t.translate?.config?.insert_method_direct_db || 'Direct DB'}
{:else}
<svg class="w-3 h-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4"/></svg>
{_t.translate?.config?.insert_method_sqllab || 'SQL Lab'}
{/if}
</span>
</div>
<RunTabContent
bind:status={m.status}
jobId={m.jobId}
@@ -243,6 +270,7 @@
completedRuns={m.completedRuns}
expandedRunIds={m.expandedRunIds}
bind:showPageBulkReplace={m.showPageBulkReplace}
schemaValidated={m.schemaValidated}
onTriggerRun={(full) => m.handleTriggerRun(full)}
onRetryRun={() => m.handleRetryRun()}
onRetryInsert={() => m.handleRetryInsert()}
@@ -250,6 +278,7 @@
onToggleRunDetails={(id) => m.toggleRunDetails(id)}
onBulkReplaceApplied={() => { m.showPageBulkReplace = false; m.loadRunHistory(); }}
onBulkReplaceClose={() => m.showPageBulkReplace = false}
onGoToTargetTab={() => m.activeTab = 'target'}
/>
{:else}
<div class="bg-surface-muted border border-border rounded-lg p-8 text-center">
@@ -257,6 +286,7 @@
</div>
{/if}
</div>
</div>
{/if}
</div>