284 lines
11 KiB
Svelte
284 lines
11 KiB
Svelte
<!-- [DEF:GitManager:Component] -->
|
||
<!--
|
||
@SEMANTICS: git, manager, dashboard, version_control, initialization
|
||
@PURPOSE: Центральный компонент для управления Git-операциями конкретного дашборда.
|
||
@LAYER: Component
|
||
@RELATION: USES -> BranchSelector
|
||
@RELATION: USES -> CommitModal
|
||
@RELATION: USES -> CommitHistory
|
||
@RELATION: USES -> DeploymentModal
|
||
@RELATION: USES -> ConflictResolver
|
||
@RELATION: CALLS -> gitService
|
||
-->
|
||
|
||
<script>
|
||
// [SECTION: IMPORTS]
|
||
import { onMount } from 'svelte';
|
||
import { gitService } from '../../services/gitService';
|
||
import { addToast as toast } from '../../lib/toasts.js';
|
||
import BranchSelector from './BranchSelector.svelte';
|
||
import CommitModal from './CommitModal.svelte';
|
||
import CommitHistory from './CommitHistory.svelte';
|
||
import DeploymentModal from './DeploymentModal.svelte';
|
||
import ConflictResolver from './ConflictResolver.svelte';
|
||
// [/SECTION]
|
||
|
||
// [SECTION: PROPS]
|
||
export let dashboardId;
|
||
export let dashboardTitle = "";
|
||
export let show = false;
|
||
// [/SECTION]
|
||
|
||
// [SECTION: STATE]
|
||
let currentBranch = 'main';
|
||
let showCommitModal = false;
|
||
let showDeployModal = false;
|
||
let showHistory = true;
|
||
let showConflicts = false;
|
||
let conflicts = [];
|
||
let loading = false;
|
||
let initialized = false;
|
||
let checkingStatus = true;
|
||
|
||
// Initialization form state
|
||
let configs = [];
|
||
let selectedConfigId = "";
|
||
let remoteUrl = "";
|
||
// [/SECTION]
|
||
|
||
// [DEF:checkStatus:Function]
|
||
/**
|
||
* @purpose Проверяет, инициализирован ли репозиторий для данного дашборда.
|
||
*/
|
||
async function checkStatus() {
|
||
checkingStatus = true;
|
||
try {
|
||
// If we can get branches, it means repo exists
|
||
await gitService.getBranches(dashboardId);
|
||
initialized = true;
|
||
} catch (e) {
|
||
initialized = false;
|
||
// Load configs if not initialized
|
||
configs = await gitService.getConfigs();
|
||
if (configs.length > 0) selectedConfigId = configs[0].id;
|
||
} finally {
|
||
checkingStatus = false;
|
||
}
|
||
}
|
||
// [/DEF:checkStatus:Function]
|
||
|
||
// [DEF:handleInit:Function]
|
||
/**
|
||
* @purpose Инициализирует репозиторий для дашборда.
|
||
*/
|
||
async function handleInit() {
|
||
if (!selectedConfigId || !remoteUrl) {
|
||
toast('Please select a Git server and provide remote URL', 'error');
|
||
return;
|
||
}
|
||
loading = true;
|
||
try {
|
||
await gitService.initRepository(dashboardId, selectedConfigId, remoteUrl);
|
||
toast('Repository initialized successfully', 'success');
|
||
initialized = true;
|
||
} catch (e) {
|
||
toast(e.message, 'error');
|
||
} finally {
|
||
loading = false;
|
||
}
|
||
}
|
||
// [/DEF:handleInit:Function]
|
||
|
||
// [DEF:handleSync:Function]
|
||
/**
|
||
* @purpose Синхронизирует состояние Superset с локальным Git-репозиторием.
|
||
*/
|
||
async function handleSync() {
|
||
loading = true;
|
||
try {
|
||
// Try to get selected environment from localStorage (set by EnvSelector)
|
||
const sourceEnvId = localStorage.getItem('selected_env_id');
|
||
await gitService.sync(dashboardId, sourceEnvId);
|
||
toast('Dashboard state synced to Git', 'success');
|
||
} catch (e) {
|
||
toast(e.message, 'error');
|
||
} finally {
|
||
loading = false;
|
||
}
|
||
}
|
||
// [/DEF:handleSync:Function]
|
||
|
||
// [DEF:handlePush:Function]
|
||
async function handlePush() {
|
||
loading = true;
|
||
try {
|
||
await gitService.push(dashboardId);
|
||
toast('Changes pushed to remote', 'success');
|
||
} catch (e) {
|
||
toast(e.message, 'error');
|
||
} finally {
|
||
loading = false;
|
||
}
|
||
}
|
||
// [/DEF:handlePush:Function]
|
||
|
||
// [DEF:handlePull:Function]
|
||
async function handlePull() {
|
||
loading = true;
|
||
try {
|
||
await gitService.pull(dashboardId);
|
||
toast('Changes pulled from remote', 'success');
|
||
} catch (e) {
|
||
toast(e.message, 'error');
|
||
} finally {
|
||
loading = false;
|
||
}
|
||
}
|
||
// [/DEF:handlePull:Function]
|
||
|
||
onMount(checkStatus);
|
||
</script>
|
||
|
||
<!-- [SECTION: TEMPLATE] -->
|
||
{#if show}
|
||
<div class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||
<div class="bg-white p-6 rounded-lg shadow-2xl w-full max-w-4xl max-h-[90vh] overflow-y-auto">
|
||
<div class="flex justify-between items-center mb-6 border-b pb-4">
|
||
<div>
|
||
<h2 class="text-2xl font-bold">Git Management: {dashboardTitle}</h2>
|
||
<p class="text-sm text-gray-500">ID: {dashboardId}</p>
|
||
</div>
|
||
<button on:click={() => show = false} class="text-gray-500 hover:text-gray-700">
|
||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
|
||
{#if checkingStatus}
|
||
<div class="flex justify-center py-12">
|
||
<div class="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
|
||
</div>
|
||
{:else if !initialized}
|
||
<div class="max-w-md mx-auto py-8">
|
||
<div class="bg-blue-50 border-l-4 border-blue-400 p-4 mb-6">
|
||
<p class="text-sm text-blue-700">
|
||
This dashboard is not yet linked to a Git repository.
|
||
Please configure the repository details below.
|
||
</p>
|
||
</div>
|
||
|
||
<div class="space-y-4">
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700">Git Server</label>
|
||
<select bind:value={selectedConfigId} class="mt-1 block w-full border rounded p-2">
|
||
{#each configs as config}
|
||
<option value={config.id}>{config.name} ({config.provider})</option>
|
||
{/each}
|
||
</select>
|
||
{#if configs.length === 0}
|
||
<p class="text-xs text-red-500 mt-1">No Git servers configured. Go to Settings -> Git to add one.</p>
|
||
{/if}
|
||
</div>
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700">Remote Repository URL</label>
|
||
<input
|
||
type="text"
|
||
bind:value={remoteUrl}
|
||
placeholder="https://github.com/org/repo.git"
|
||
class="mt-1 block w-full border rounded p-2"
|
||
/>
|
||
</div>
|
||
<button
|
||
on:click={handleInit}
|
||
disabled={loading || configs.length === 0}
|
||
class="w-full bg-blue-600 text-white py-2 rounded font-medium hover:bg-blue-700 disabled:opacity-50"
|
||
>
|
||
{loading ? 'Initializing...' : 'Initialize Repository'}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
{:else}
|
||
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||
<!-- Left Column: Controls -->
|
||
<div class="md:col-span-1 space-y-6">
|
||
<section>
|
||
<h3 class="text-sm font-semibold text-gray-400 uppercase tracking-wider mb-2">Branch</h3>
|
||
<BranchSelector {dashboardId} bind:currentBranch />
|
||
</section>
|
||
|
||
<section class="space-y-2">
|
||
<h3 class="text-sm font-semibold text-gray-400 uppercase tracking-wider mb-2">Actions</h3>
|
||
<button
|
||
on:click={handleSync}
|
||
disabled={loading}
|
||
class="w-full flex items-center justify-center px-4 py-2 bg-gray-100 hover:bg-gray-200 rounded text-sm font-medium transition"
|
||
>
|
||
Sync from Superset
|
||
</button>
|
||
<button
|
||
on:click={() => showCommitModal = true}
|
||
disabled={loading}
|
||
class="w-full flex items-center justify-center px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded text-sm font-medium transition"
|
||
>
|
||
Commit Changes
|
||
</button>
|
||
<div class="grid grid-cols-2 gap-2">
|
||
<button
|
||
on:click={handlePull}
|
||
disabled={loading}
|
||
class="flex items-center justify-center px-4 py-2 border hover:bg-gray-50 rounded text-sm font-medium transition"
|
||
>
|
||
Pull
|
||
</button>
|
||
<button
|
||
on:click={handlePush}
|
||
disabled={loading}
|
||
class="flex items-center justify-center px-4 py-2 border hover:bg-gray-50 rounded text-sm font-medium transition"
|
||
>
|
||
Push
|
||
</button>
|
||
</div>
|
||
</section>
|
||
|
||
<section>
|
||
<h3 class="text-sm font-semibold text-gray-400 uppercase tracking-wider mb-2">Deployment</h3>
|
||
<button
|
||
on:click={() => showDeployModal = true}
|
||
disabled={loading}
|
||
class="w-full flex items-center justify-center px-4 py-2 bg-green-600 hover:bg-green-700 text-white rounded text-sm font-medium transition"
|
||
>
|
||
Deploy to Environment
|
||
</button>
|
||
</section>
|
||
</div>
|
||
|
||
<!-- Right Column: History -->
|
||
<div class="md:col-span-2 border-l pl-6">
|
||
<CommitHistory {dashboardId} />
|
||
</div>
|
||
</div>
|
||
{/if}
|
||
</div>
|
||
</div>
|
||
{/if}
|
||
|
||
<CommitModal
|
||
{dashboardId}
|
||
bind:show={showCommitModal}
|
||
on:commit={() => { /* Refresh history */ }}
|
||
/>
|
||
|
||
<DeploymentModal
|
||
{dashboardId}
|
||
bind:show={showDeployModal}
|
||
/>
|
||
|
||
<ConflictResolver
|
||
{conflicts}
|
||
bind:show={showConflicts}
|
||
on:resolve={() => { /* Handle resolution */ }}
|
||
/>
|
||
<!-- [/SECTION] -->
|
||
|
||
<!-- [/DEF:GitManager:Component] --> |