This commit is contained in:
2026-05-26 18:15:37 +03:00
parent 0b6f61623e
commit ee08e2f3dd
15 changed files with 13702 additions and 7702 deletions

View File

@@ -1,58 +1,44 @@
<!-- #region BranchSelector [C:3] [TYPE Component] [SEMANTICS git, branch, checkout, create, selector] -->
<!-- @BRIEF Component component: components/git/BranchSelector.svelte -->
<!-- @BRIEF UI for selecting and creating Git branches for a dashboard repository. -->
<!-- @LAYER UI -->
<!--
@SEMANTICS: git, branch, selection, checkout
@PURPOSE: UI для выбора и создания веток Git.
@LAYER Component
@RELATION CALLS -> [gitService.getBranches]
@RELATION CALLS -> [GitServiceBranch]
@RELATION CALLS -> [gitService.createBranch]
@RELATION BINDS_TO -> [onchange]
-->
<!-- @RELATION CALLS -> [EXT:frontend:gitService.getBranches] -->
<!-- @RELATION CALLS -> [EXT:frontend:gitService.checkoutBranch] -->
<!-- @RELATION CALLS -> [EXT:frontend:gitService.createBranch] -->
<!-- @RELATION BINDS_TO -> [onchange] -->
<!-- @PRE dashboardId is a valid dashboard slug; envId required when ref is slug. -->
<!-- @UX_STATE Loading -> Select disabled, spinner. -->
<!-- @UX_STATE Loaded -> Branch dropdown enabled. -->
<!-- @UX_STATE CreateMode -> Inline input for new branch name. -->
<script>
// [SECTION: IMPORTS]
import { onMount } from 'svelte';
import { gitService } from '../../services/gitService';
import { addToast as toast } from '../../lib/toasts.js';
import { t } from '../../lib/i18n';
import { Button, Select, Input } from '../../lib/ui';
// [/SECTION]
// [SECTION: PROPS]
let {
dashboardId,
envId = null,
currentBranch = $bindable('main'),
onchange = () => {},
} = $props();
// #region props [C:2] [TYPE Block]
// @PRE dashboardId is non-empty slug; envId resolved via GitManager fallback chain.
let {
dashboardId,
envId = null,
currentBranch = $bindable('main'),
onchange = () => {},
} = $props();
// #endregion props
// [/SECTION]
// [SECTION: STATE]
// #region state [C:1] [TYPE Block]
let branches = $state([]);
let loading = $state(false);
let showCreate = $state(false);
let newBranchName = $state('');
// [/SECTION]
// #region onMount:Function [TYPE Function]
/**
* @purpose Load branches when component is mounted.
* @pre Component is initialized.
* @post loadBranches is called.
*/
onMount(async () => {
await loadBranches();
});
// #endregion onMount:Function
// #endregion state
// #region loadBranches:Function [TYPE Function]
/**
* @purpose Загружает список веток для дашборда.
* @pre dashboardId is provided.
* @post branches обновлен.
*/
// #region loadBranches [C:2] [TYPE Function]
// @BRIEF Load branch list from backend.
// @PRE dashboardId is non-empty; envId required when ref is slug.
// @POST branches array populated with BranchSchema objects.
// @SIDE_EFFECT GETs /git/repositories/{ref}/branches.
async function loadBranches() {
console.log(`[EXT:frontend:BranchSelector][Action] Loading branches for dashboard ${dashboardId}`);
loading = true;
@@ -66,25 +52,22 @@
loading = false;
}
}
// #endregion loadBranches:Function
// #endregion loadBranches
// #region handleSelect:Function [TYPE Function]
/**
* @purpose Handles branch selection from dropdown.
* @pre event contains branch name.
* @post handleCheckout is called with selected branch.
*/
// #region handleSelect [C:1] [TYPE Function]
// @BRIEF Handle branch selection from dropdown.
// @PRE event contains target.value with branch name.
// @POST handleCheckout called with selected branch name.
function handleSelect(event) {
handleCheckout(event.target.value);
}
// #endregion handleSelect:Function
// #endregion handleSelect
// #region handleCheckout:Function [TYPE Function]
/**
* @purpose Переключает текущую ветку.
* @param {string} branchName - Имя ветки.
* @post currentBranch обновлен, родительский callback вызван.
*/
// #region handleCheckout [C:2] [TYPE Function]
// @BRIEF Switch repository to selected branch.
// @PRE branchName is non-empty; envId required when ref is slug.
// @POST currentBranch updated; onchange callback fired.
// @SIDE_EFFECT POSTs to /git/repositories/{ref}/checkout.
async function handleCheckout(branchName) {
console.log(`[EXT:frontend:BranchSelector][Action] Checking out branch ${branchName}`);
try {
@@ -98,14 +81,13 @@
toast(e.message, 'error');
}
}
// #endregion handleCheckout:Function
// #endregion handleCheckout
// #region handleCreate:Function [TYPE Function]
/**
* @purpose Создает новую ветку.
* @pre newBranchName is not empty.
* @post Новая ветка создана и загружена; showCreate reset.
*/
// #region handleCreate [C:2] [TYPE Function]
// @BRIEF Create a new branch from current branch.
// @PRE newBranchName is non-empty; envId required when ref is slug.
// @POST New branch created; branch list reloaded; create mode closed.
// @SIDE_EFFECT POSTs to /git/repositories/{ref}/branches.
async function handleCreate() {
if (!newBranchName) return;
console.log(`[EXT:frontend:BranchSelector][Action] Creating branch ${newBranchName} from ${currentBranch}`);
@@ -121,7 +103,11 @@
toast(e.message, 'error');
}
}
// #endregion handleCreate:Function
// #endregion handleCreate
onMount(async () => {
await loadBranches();
});
</script>
<!-- [SECTION: TEMPLATE] -->