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:
@@ -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}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user