feat(git): implement deployment tracking and enhance lifecycle UX
Introduce a deployment recording system to track dashboard versions across environments and improve the Git management user experience. - Add `Deployment` model and Alembic migration to persist deployment history. - Implement `GitDeploymentRecorder` and `GitFingerprint` plugins to automate deployment logging and content hashing. - Add `get_deployment_status` API endpoint to retrieve real-time environment states. - Refactor `GitLifecycleHeader` to prioritize Call-to-Action (CTA) buttons and improve visual hierarchy. - Update `GitWorkspacePanel` to emphasize version saving and streamline commit workflows. - Enhance `GitEnvironmentTimeline` with deployment status integration, collapsible UI, and improved theme consistency. - Add auto-navigation logic in `GitManagerModel` to guide users to relevant tabs based on recommended actions. - Clean up obsolete documentation and update i18n strings for git visualization features.
This commit is contained in:
@@ -91,24 +91,39 @@
|
||||
|
||||
function handleRecommendedAction() {
|
||||
if (model.recommendedAction === 'sync') {
|
||||
void model.handleSync();
|
||||
model.autoNavigateTab = 'workspace';
|
||||
model.activeTab = 'workspace';
|
||||
void model.handleSync();
|
||||
return;
|
||||
}
|
||||
if (model.recommendedAction === 'commit') {
|
||||
model.autoNavigateTab = 'workspace';
|
||||
model.activeTab = 'workspace';
|
||||
return;
|
||||
}
|
||||
if (model.recommendedAction === 'promote') {
|
||||
model.autoNavigateTab = 'release';
|
||||
model.activeTab = 'release';
|
||||
return;
|
||||
}
|
||||
model.autoNavigateTab = 'operations';
|
||||
model.activeTab = 'operations';
|
||||
}
|
||||
|
||||
$effect(() => {
|
||||
if (model.showDeployConfirm) deployConfirmInput = '';
|
||||
});
|
||||
// ── Auto-navigate tab pulse indicator ──
|
||||
let pulseTab: string | null = $state(null);
|
||||
$effect(() => {
|
||||
if (model.autoNavigateTab) {
|
||||
pulseTab = model.autoNavigateTab;
|
||||
model.autoNavigateTab = null;
|
||||
const timer = setTimeout(() => { pulseTab = null; }, 2000);
|
||||
return () => clearTimeout(timer);
|
||||
}
|
||||
});
|
||||
|
||||
function handleBackdropClick(e) { if (e.target === e.currentTarget) closeModal(); }
|
||||
|
||||
onMount(() => {
|
||||
@@ -151,7 +166,7 @@
|
||||
class="inline-flex items-center gap-1.5 rounded-lg px-3 py-1.5 text-xs font-medium text-text-muted transition-colors hover:bg-surface-muted hover:text-text disabled:opacity-50"
|
||||
aria-label={$t.common?.refresh || 'Refresh'}
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-3.5 w-3.5 {model.checkingStatus ? 'animate-spin' : ''}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182M16.023 9.348H20M14.985 19.644H20M2.985 14.652V20" /></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" class="h-3.5 w-3.5 {model.checkingStatus ? 'animate-spin' : ''}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182M16.023 9.348H20M14.985 19.644H20M2.985 14.652V20" /></svg>
|
||||
{$t.common?.refresh || 'Refresh'}
|
||||
</button>
|
||||
<button type="button" onclick={closeModal} class="flex h-9 w-9 items-center justify-center rounded-lg text-text-subtle transition-colors hover:bg-surface-muted hover:text-text" aria-label={$t.common?.close || 'Close'}>
|
||||
@@ -254,7 +269,7 @@
|
||||
|
||||
<!-- Branch selector moved here per UX review: separate from version map to avoid confusion -->
|
||||
<div class="flex items-center gap-3 mb-2">
|
||||
<div class="text-xs font-medium text-text-muted uppercase tracking-wide">Рабочая ветка (текущие изменения):</div>
|
||||
<div class="text-xs font-medium text-text-muted uppercase tracking-wide">{$t.git?.working_branch || 'Working branch (current changes):'}</div>
|
||||
<div class="flex-1 max-w-[280px]">
|
||||
<BranchSelector
|
||||
{dashboardId}
|
||||
@@ -271,6 +286,7 @@
|
||||
branches={model.branches}
|
||||
environmentHistories={model.environmentHistories}
|
||||
historiesLoading={model.environmentHistoriesLoading}
|
||||
deploymentStatus={model.deploymentStatus}
|
||||
{dashboardId}
|
||||
envId={model.resolvedEnvId}
|
||||
bind:currentBranch={model.currentBranch}
|
||||
@@ -291,17 +307,17 @@
|
||||
/>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-1 border-b border-border pb-0" role="tablist" aria-label={$t.git?.management || 'Управление Git'}>
|
||||
<button class={`relative -mb-px inline-flex items-center gap-2 rounded-t-lg px-4 py-2.5 text-sm font-medium transition-colors ${model.activeTab === 'workspace' ? 'border border-b-white bg-surface-card text-primary shadow-sm' : 'border border-transparent text-text-muted hover:bg-surface-muted hover:text-text'}`} onclick={() => (model.activeTab = 'workspace')} role="tab" aria-selected={model.activeTab === 'workspace'} aria-controls="git-tab-panel" id="git-tab-workspace" tabindex={model.activeTab === 'workspace' ? 0 : -1}>
|
||||
<button class={`relative -mb-px inline-flex items-center gap-2 rounded-t-lg px-4 py-2.5 text-sm font-medium transition-colors focus-visible:ring-2 focus-visible:ring-primary-ring ${model.activeTab === 'workspace' ? 'border border-b-white bg-surface-card text-primary shadow-sm' : 'border border-transparent text-text-muted hover:bg-surface-muted hover:text-text'} ${pulseTab === 'workspace' ? 'animate-pulse ring-2 ring-primary-ring' : ''}`} onclick={() => (model.activeTab = 'workspace')} role="tab" aria-selected={model.activeTab === 'workspace'} aria-controls="git-tab-panel" id="git-tab-workspace" tabindex={model.activeTab === 'workspace' ? 0 : -1}>
|
||||
<Icon name="edit" size={16} strokeWidth={2} />
|
||||
{$t.git?.tab_workspace || 'Изменения'}
|
||||
<HelpTooltip text={$t.git?.hint_workspace || ''} ariaLabel={$t.git?.help_aria || 'Справка'} />
|
||||
</button>
|
||||
<button class={`relative -mb-px inline-flex items-center gap-2 rounded-t-lg px-4 py-2.5 text-sm font-medium transition-colors ${model.activeTab === 'release' ? 'border border-b-white bg-surface-card text-primary shadow-sm' : 'border border-transparent text-text-muted hover:bg-surface-muted hover:text-text'}`} onclick={() => (model.activeTab = 'release')} role="tab" aria-selected={model.activeTab === 'release'} aria-controls="git-tab-panel" id="git-tab-release" tabindex={model.activeTab === 'release' ? 0 : -1}>
|
||||
<button class={`relative -mb-px inline-flex items-center gap-2 rounded-t-lg px-4 py-2.5 text-sm font-medium transition-colors focus-visible:ring-2 focus-visible:ring-primary-ring ${model.activeTab === 'release' ? 'border border-b-white bg-surface-card text-primary shadow-sm' : 'border border-transparent text-text-muted hover:bg-surface-muted hover:text-text'} ${pulseTab === 'release' ? 'animate-pulse ring-2 ring-primary-ring' : ''}`} onclick={() => (model.activeTab = 'release')} role="tab" aria-selected={model.activeTab === 'release'} aria-controls="git-tab-panel" id="git-tab-release" tabindex={model.activeTab === 'release' ? 0 : -1}>
|
||||
<Icon name="lightning" size={16} strokeWidth={2} />
|
||||
{$t.git?.tab_release || 'Релиз'}
|
||||
<HelpTooltip text={$t.git?.hint_release || ''} ariaLabel={$t.git?.help_aria || 'Справка'} />
|
||||
</button>
|
||||
<button class={`relative -mb-px inline-flex items-center gap-2 rounded-t-lg px-4 py-2.5 text-sm font-medium transition-colors ${model.activeTab === 'operations' ? 'border border-b-white bg-surface-card text-primary shadow-sm' : 'border border-transparent text-text-muted hover:bg-surface-muted hover:text-text'}`} onclick={() => (model.activeTab = 'operations')} role="tab" aria-selected={model.activeTab === 'operations'} aria-controls="git-tab-panel" id="git-tab-operations" tabindex={model.activeTab === 'operations' ? 0 : -1}>
|
||||
<button class={`relative -mb-px inline-flex items-center gap-2 rounded-t-lg px-4 py-2.5 text-sm font-medium transition-colors focus-visible:ring-2 focus-visible:ring-primary-ring ${model.activeTab === 'operations' ? 'border border-b-white bg-surface-card text-primary shadow-sm' : 'border border-transparent text-text-muted hover:bg-surface-muted hover:text-text'} ${pulseTab === 'operations' ? 'animate-pulse ring-2 ring-primary-ring' : ''}`} onclick={() => (model.activeTab = 'operations')} role="tab" aria-selected={model.activeTab === 'operations'} aria-controls="git-tab-panel" id="git-tab-operations" tabindex={model.activeTab === 'operations' ? 0 : -1}>
|
||||
<Icon name="settings" size={16} strokeWidth={2} />
|
||||
{$t.git?.tab_operations || 'Серверные операции'}
|
||||
<HelpTooltip text={$t.git?.hint_operations || ''} ariaLabel={$t.git?.help_aria || 'Справка'} />
|
||||
|
||||
Reference in New Issue
Block a user