refactor(frontend): migrate badge patterns to Badge component (8 files)
Replaced inline rounded-full badge patterns and removed 5 duplicated helper functions: - getStatusBadgeClass (translate/+page, validation-tasks/[policyId]) - getRunStatusBadgeClass (validation-tasks/[policyId]) - getStateClass (LaunchConfirmationPanel) - getStateTone (CompiledSQLPreview) All badges now use <Badge variant="..."> from $lib/ui.
This commit is contained in:
@@ -22,6 +22,7 @@
|
||||
<script lang="ts">
|
||||
import { t } from "$lib/i18n/index.svelte.js";
|
||||
import { requestDatasetReviewApi } from "$lib/api/datasetReview.js";
|
||||
import { Badge } from "$lib/ui";
|
||||
|
||||
let {
|
||||
sessionId = "",
|
||||
@@ -71,21 +72,7 @@
|
||||
return $t.dataset_review?.preview?.state?.[state] || state;
|
||||
}
|
||||
|
||||
function getStateTone(state) {
|
||||
if (state === "ready") {
|
||||
return "bg-success-light text-success";
|
||||
}
|
||||
if (state === "stale") {
|
||||
return "bg-warning-light text-warning";
|
||||
}
|
||||
if (state === "failed") {
|
||||
return "bg-destructive-light text-destructive";
|
||||
}
|
||||
if (state === "pending") {
|
||||
return "bg-primary-light text-primary";
|
||||
}
|
||||
return "bg-surface-muted text-text";
|
||||
}
|
||||
|
||||
|
||||
function getCompilerLabel() {
|
||||
if (compiledBySuperset) {
|
||||
@@ -197,9 +184,9 @@
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<span class={`rounded-full px-3 py-1 text-xs font-medium ${getStateTone(effectiveState)}`}>
|
||||
<Badge variant={effectiveState === "ready" ? "success" : effectiveState === "stale" ? "warning" : effectiveState === "failed" ? "destructive" : effectiveState === "pending" ? "primary" : "muted"}>
|
||||
{$t.dataset_review?.preview?.state_label}: {getStateLabel(effectiveState)}
|
||||
</span>
|
||||
</Badge>
|
||||
<button
|
||||
type="button"
|
||||
class="inline-flex items-center justify-center rounded-xl bg-primary px-4 py-2 text-sm font-medium text-white transition hover:bg-primary-hover disabled:cursor-not-allowed disabled:bg-border"
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
<script lang="ts">
|
||||
import { t } from "$lib/i18n/index.svelte.js";
|
||||
import { requestDatasetReviewApi } from "$lib/api/datasetReview.js";
|
||||
import { Badge } from "$lib/ui";
|
||||
|
||||
let {
|
||||
sessionId = "",
|
||||
@@ -377,23 +378,11 @@
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<span
|
||||
class={`rounded-full px-3 py-1 text-xs font-medium ${
|
||||
reviewState === "Incomplete"
|
||||
? "bg-destructive-light text-destructive"
|
||||
: reviewState === "WarningApproval"
|
||||
? "bg-warning-light text-warning"
|
||||
: "bg-success-light text-success"
|
||||
}`}
|
||||
>
|
||||
<Badge variant={reviewState === "Incomplete" ? "destructive" : reviewState === "WarningApproval" ? "warning" : "success"}>
|
||||
{$t.dataset_review?.mapping?.state_label}: {$t.dataset_review?.mapping?.state?.[reviewState]}
|
||||
</span>
|
||||
<span class="rounded-full bg-surface-muted px-3 py-1 text-xs font-medium text-text">
|
||||
{$t.dataset_review?.mapping?.pending_approvals_label}: {pendingApprovalCount}
|
||||
</span>
|
||||
<span class="rounded-full bg-surface-muted px-3 py-1 text-xs font-medium text-text">
|
||||
{$t.dataset_review?.mapping?.required_values_label}: {missingRequiredCount}
|
||||
</span>
|
||||
</Badge>
|
||||
<Badge variant="muted">{$t.dataset_review?.mapping?.pending_approvals_label}: {pendingApprovalCount}</Badge>
|
||||
<Badge variant="muted">{$t.dataset_review?.mapping?.required_values_label}: {missingRequiredCount}</Badge>
|
||||
<button
|
||||
type="button"
|
||||
class="inline-flex items-center justify-center rounded-xl border border-border-strong px-4 py-2 text-sm font-medium text-text transition hover:bg-surface-page disabled:cursor-not-allowed disabled:opacity-50"
|
||||
@@ -459,24 +448,24 @@
|
||||
<h3 class="text-base font-semibold text-text">
|
||||
{importedFilter?.display_name || importedFilter?.filter_name || mapping.mapping_id}
|
||||
</h3>
|
||||
<span class="rounded-full bg-surface-muted px-2 py-0.5 text-xs text-text">
|
||||
<Badge variant="muted" size="sm">
|
||||
{$t.dataset_review?.mapping?.to_variable_label}: {templateVariable?.variable_name ||
|
||||
mapping.variable_id}
|
||||
</span>
|
||||
</Badge>
|
||||
{#if templateVariable?.is_required}
|
||||
<span class="rounded-full bg-destructive-light px-2 py-0.5 text-xs text-destructive">
|
||||
<Badge variant="destructive" size="sm">
|
||||
{$t.dataset_review?.mapping?.required_badge}
|
||||
</span>
|
||||
</Badge>
|
||||
{/if}
|
||||
{#if rowState.needsExplicitApproval}
|
||||
<span class="rounded-full bg-warning-light px-2 py-0.5 text-xs text-warning">
|
||||
<Badge variant="warning" size="sm">
|
||||
{$t.dataset_review?.mapping?.approval_required_badge}
|
||||
</span>
|
||||
</Badge>
|
||||
{/if}
|
||||
{#if mapping.approval_state === "approved"}
|
||||
<span class="rounded-full bg-success-light px-2 py-0.5 text-xs text-success">
|
||||
<Badge variant="success" size="sm">
|
||||
{$t.dataset_review?.mapping?.approved_badge}
|
||||
</span>
|
||||
</Badge>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
<script lang="ts">
|
||||
import { t } from "$lib/i18n/index.svelte.js";
|
||||
import { requestDatasetReviewApi } from "$lib/api/datasetReview.js";
|
||||
import { Badge } from "$lib/ui";
|
||||
|
||||
let {
|
||||
sessionId = "",
|
||||
@@ -148,15 +149,7 @@
|
||||
return $t.dataset_review?.launch?.state?.[state] || state;
|
||||
}
|
||||
|
||||
function getStateClass(state) {
|
||||
if (state === "Submitted") {
|
||||
return "bg-success-light text-success";
|
||||
}
|
||||
if (state === "Ready") {
|
||||
return "bg-primary-light text-primary";
|
||||
}
|
||||
return "bg-destructive-light text-destructive";
|
||||
}
|
||||
|
||||
|
||||
function jumpTo(target) {
|
||||
onjump({ target });
|
||||
@@ -211,9 +204,9 @@
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<span class={`rounded-full px-3 py-1 text-xs font-medium ${getStateClass(panelState)}`}>
|
||||
<Badge variant={panelState === "Submitted" ? "success" : panelState === "Ready" ? "primary" : "destructive"}>
|
||||
{$t.dataset_review?.launch?.state_label}: {getStateLabel(panelState)}
|
||||
</span>
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
{#if panelState === "Blocked"}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { t } from "$lib/i18n/index.svelte.js";
|
||||
import { Badge } from "$lib/ui";
|
||||
|
||||
let {
|
||||
findings = [],
|
||||
@@ -179,9 +180,7 @@
|
||||
<h3 class="text-sm font-semibold text-text">
|
||||
{$t.dataset_review?.findings?.blocking_title}
|
||||
</h3>
|
||||
<span class="rounded-full bg-destructive-light px-2.5 py-1 text-xs font-medium text-destructive">
|
||||
{blockingFindings.length}
|
||||
</span>
|
||||
<Badge variant="destructive">{blockingFindings.length}</Badge>
|
||||
</div>
|
||||
|
||||
{#if blockingFindings.length === 0}
|
||||
@@ -271,9 +270,7 @@
|
||||
<h3 class="text-sm font-semibold text-text">
|
||||
{$t.dataset_review?.findings?.warning_title}
|
||||
</h3>
|
||||
<span class="rounded-full bg-warning-light px-2.5 py-1 text-xs font-medium text-warning">
|
||||
{warningFindings.length}
|
||||
</span>
|
||||
<Badge variant="warning">{warningFindings.length}</Badge>
|
||||
</div>
|
||||
|
||||
{#if warningFindings.length === 0}
|
||||
@@ -363,9 +360,7 @@
|
||||
<h3 class="text-sm font-semibold text-text">
|
||||
{$t.dataset_review?.findings?.informational_title}
|
||||
</h3>
|
||||
<span class="rounded-full bg-surface-muted px-2.5 py-1 text-xs font-medium text-text">
|
||||
{informationalFindings.length}
|
||||
</span>
|
||||
<Badge variant="muted">{informationalFindings.length}</Badge>
|
||||
</div>
|
||||
|
||||
{#if informationalFindings.length === 0}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
<!-- @LAYER UI -->
|
||||
<script lang="ts">
|
||||
import { t } from "$lib/i18n/index.svelte.js";
|
||||
import { Badge } from "$lib/ui";
|
||||
|
||||
let { session, currentWorkspaceState, readinessLabel, getWorkspaceStateLabel, profile } = $props();
|
||||
</script>
|
||||
@@ -23,20 +24,12 @@
|
||||
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
{#if session}
|
||||
<span class="rounded-full bg-surface-muted px-3 py-1 text-xs font-medium text-text">
|
||||
{session.source_kind || $t.dataset_review?.workspace?.source_badge_fallback}
|
||||
</span>
|
||||
<span class="rounded-full bg-surface-muted px-3 py-1 text-xs font-medium text-text">
|
||||
{profile?.dataset_name || session.dataset_ref}
|
||||
</span>
|
||||
<Badge variant="muted">{session.source_kind || $t.dataset_review?.workspace?.source_badge_fallback}</Badge>
|
||||
<Badge variant="muted">{profile?.dataset_name || session.dataset_ref}</Badge>
|
||||
{/if}
|
||||
<span class="rounded-full bg-surface-muted px-3 py-1 text-xs font-medium text-text">
|
||||
{$t.dataset_review?.workspace?.state_label}: {getWorkspaceStateLabel(currentWorkspaceState)}
|
||||
</span>
|
||||
<Badge variant="muted">{$t.dataset_review?.workspace?.state_label}: {getWorkspaceStateLabel(currentWorkspaceState)}</Badge>
|
||||
{#if session}
|
||||
<span class="rounded-full bg-primary-light px-3 py-1 text-xs font-medium text-primary">
|
||||
{$t.dataset_review?.workspace?.readiness_label}: {readinessLabel}
|
||||
</span>
|
||||
<Badge variant="primary">{$t.dataset_review?.workspace?.readiness_label}: {readinessLabel}</Badge>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
import { page } from "$app/state";
|
||||
import { goto } from "$app/navigation";
|
||||
import { t } from "$lib/i18n/index.svelte.js";
|
||||
import { Button } from "$lib/ui";
|
||||
import { Badge, Button } from "$lib/ui";
|
||||
import SourceIntakePanel from "$lib/components/dataset-review/SourceIntakePanel.svelte";
|
||||
import ValidationFindingsPanel from "$lib/components/dataset-review/ValidationFindingsPanel.svelte";
|
||||
import SemanticLayerReview from "$lib/components/dataset-review/SemanticLayerReview.svelte";
|
||||
@@ -207,20 +207,11 @@
|
||||
<div class="mt-0.5 text-sm font-semibold text-primary">{getRecommendedActionLabel($t, m.session?.recommended_action)}</div>
|
||||
</div>
|
||||
{#if m.session?.readiness_state === "run_ready"}
|
||||
<span class="inline-flex items-center gap-1.5 rounded-full bg-success-light px-3 py-1 text-xs font-medium text-success">
|
||||
<span class="h-1.5 w-1.5 rounded-full bg-success"></span>
|
||||
Готов к запуску
|
||||
</span>
|
||||
<Badge variant="success" dot={true}>Готов к запуску</Badge>
|
||||
{:else if m.session?.readiness_state === "review_ready"}
|
||||
<span class="inline-flex items-center gap-1.5 rounded-full bg-primary-light px-3 py-1 text-xs font-medium text-primary">
|
||||
<span class="h-1.5 w-1.5 rounded-full bg-primary-ring"></span>
|
||||
Готов к проверке
|
||||
</span>
|
||||
<Badge variant="primary" dot={true}>Готов к проверке</Badge>
|
||||
{:else if m.session?.readiness_state === "recovery_required" || m.session?.readiness_state === "partially_ready"}
|
||||
<span class="inline-flex items-center gap-1.5 rounded-full bg-warning-light px-3 py-1 text-xs font-medium text-warning">
|
||||
<span class="h-1.5 w-1.5 rounded-full bg-warning"></span>
|
||||
Частичное восстановление
|
||||
</span>
|
||||
<Badge variant="warning" dot={true}>Частичное восстановление</Badge>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
@@ -229,7 +220,7 @@
|
||||
<div class="flex items-center gap-2">
|
||||
<svg class="h-4 w-4 text-text-subtle" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z"></path></svg>
|
||||
<span class="text-xs font-semibold uppercase tracking-wide text-text-muted">{$t.dataset_review?.workspace?.recovered_filters_title || "Фильтры из дашборда"}</span>
|
||||
<span class="rounded-full bg-primary-light px-2 py-0.5 text-xs font-medium text-primary">{m.importedFilters.length}</span>
|
||||
<Badge variant="primary" size="sm">{m.importedFilters.length}</Badge>
|
||||
</div>
|
||||
<div class="mt-2 flex flex-wrap gap-2">
|
||||
{#each m.importedFilters.slice(0, 6) as filter}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import { ROUTES } from '$lib/routes';
|
||||
import { t, _ } from '$lib/i18n/index.svelte.js';
|
||||
import { addToast } from '$lib/toasts.svelte.js';
|
||||
import { Button, EmptyState } from '$lib/ui';
|
||||
import { Badge, Button, EmptyState } from '$lib/ui';
|
||||
import { fetchJobs, deleteJob, duplicateJob } from '$lib/api/translate.js';
|
||||
|
||||
/** @type {string} idle | loading | empty | populated | error */
|
||||
@@ -98,18 +98,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
/** @param {string} status */
|
||||
function getStatusBadgeClass(status) {
|
||||
const map = {
|
||||
DRAFT: 'bg-surface-muted text-text',
|
||||
READY: 'bg-primary-light text-primary',
|
||||
RUNNING: 'bg-warning-light text-warning',
|
||||
COMPLETED: 'bg-success-light text-success',
|
||||
FAILED: 'bg-destructive-light text-destructive',
|
||||
CANCELLED: 'bg-surface-muted text-text-muted',
|
||||
};
|
||||
return map[status] || 'bg-surface-muted text-text';
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<div class="container mx-auto px-4 py-6">
|
||||
@@ -196,6 +185,7 @@
|
||||
{:else if uxState === 'populated'}
|
||||
<div class="grid gap-4">
|
||||
{#each jobs as job}
|
||||
{@const statusVariant = ({ DRAFT: "muted", READY: "primary", RUNNING: "warning", COMPLETED: "success", FAILED: "destructive", CANCELLED: "muted" })[job.status] || "muted"}
|
||||
<div
|
||||
onclick={() => navigateToConfig(job.id)}
|
||||
class="bg-surface-card border border-border rounded-lg p-4 hover:shadow-md hover:border-border-strong transition-all cursor-pointer"
|
||||
@@ -204,9 +194,7 @@
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="flex items-center gap-3 mb-1">
|
||||
<h3 class="text-lg font-semibold text-text truncate">{job.name}</h3>
|
||||
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium {getStatusBadgeClass(job.status)}">
|
||||
{job.status}
|
||||
</span>
|
||||
<Badge variant={statusVariant}>{job.status}</Badge>
|
||||
</div>
|
||||
{#if job.description}
|
||||
<p class="text-sm text-text-muted truncate mb-2">{job.description}</p>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
import { goto } from '$app/navigation';
|
||||
import { ROUTES } from '$lib/routes';
|
||||
import { t } from '$lib/i18n/index.svelte.js';
|
||||
import { EmptyState } from '$lib/ui';
|
||||
import { Badge, EmptyState } from '$lib/ui';
|
||||
import { addToast } from '$lib/toasts.svelte.js';
|
||||
import { triggerValidationRun as triggerRun, deleteValidationTask as deleteTask, getValidationRuns as fetchRuns } from '$lib/api.js';
|
||||
import { page } from '$app/state';
|
||||
@@ -43,22 +43,7 @@
|
||||
// Derived status
|
||||
let isScheduled = $derived(task?.schedule_days?.length > 0);
|
||||
|
||||
/** @param {string} status */
|
||||
function getStatusBadgeClass(status) {
|
||||
const map = {
|
||||
PASS: 'bg-success-light text-success',
|
||||
WARN: 'bg-warning-light text-warning',
|
||||
FAIL: 'bg-destructive-light text-destructive',
|
||||
UNKNOWN: 'bg-surface-muted text-text-muted',
|
||||
RUNNING: 'bg-primary-light text-primary',
|
||||
PENDING: 'bg-warning-light text-warning',
|
||||
COMPLETED: 'bg-success-light text-success',
|
||||
};
|
||||
return map[status] || 'bg-surface-muted text-text-muted';
|
||||
}
|
||||
|
||||
/** @param {string} status */
|
||||
const getRunStatusBadgeClass = getStatusBadgeClass;
|
||||
|
||||
/** @param {number} ms */
|
||||
function formatDuration(ms) {
|
||||
@@ -166,22 +151,14 @@
|
||||
<div class="flex items-center gap-3 mb-2">
|
||||
<h1 class="text-2xl font-bold text-text truncate">{task?.name || '-'}</h1>
|
||||
{#if task?.is_active !== false}
|
||||
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-success-light text-success">
|
||||
{$t.validation?.active || 'Active'}
|
||||
</span>
|
||||
<Badge variant="success">{$t.validation?.active || 'Active'}</Badge>
|
||||
{:else}
|
||||
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-surface-muted text-text-muted">
|
||||
{$t.validation?.inactive || 'Inactive'}
|
||||
</span>
|
||||
<Badge variant="muted">{$t.validation?.inactive || 'Inactive'}</Badge>
|
||||
{/if}
|
||||
{#if isScheduled}
|
||||
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-primary-light text-primary">
|
||||
{$t.validation?.scheduled || 'Scheduled'}
|
||||
</span>
|
||||
<Badge variant="primary">{$t.validation?.scheduled || 'Scheduled'}</Badge>
|
||||
{:else}
|
||||
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-surface-muted text-text-muted">
|
||||
{$t.validation?.manual_only || 'Manual only'}
|
||||
</span>
|
||||
<Badge variant="muted">{$t.validation?.manual_only || 'Manual only'}</Badge>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="flex flex-wrap items-center gap-x-4 gap-y-1 text-sm text-text-muted">
|
||||
@@ -342,9 +319,7 @@
|
||||
{getTriggerLabel(run.trigger)}
|
||||
</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap">
|
||||
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium {getRunStatusBadgeClass(run.status)}">
|
||||
{run.status || 'UNKNOWN'}
|
||||
</span>
|
||||
<Badge variant={({ PASS: "success", WARN: "warning", FAIL: "destructive", UNKNOWN: "muted", RUNNING: "primary", PENDING: "warning", COMPLETED: "success" })[run.status] || "muted"}>{run.status || 'UNKNOWN'}</Badge>
|
||||
</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-sm text-text-muted">
|
||||
{getAggregateSummary(run)}
|
||||
|
||||
Reference in New Issue
Block a user