slug first logic
This commit is contained in:
@@ -21,6 +21,7 @@
|
||||
// [SECTION: PROPS]
|
||||
let {
|
||||
dashboardId,
|
||||
envId = null,
|
||||
currentBranch = 'main',
|
||||
} = $props();
|
||||
|
||||
@@ -56,7 +57,7 @@
|
||||
console.log(`[BranchSelector][Action] Loading branches for dashboard ${dashboardId}`);
|
||||
loading = true;
|
||||
try {
|
||||
branches = await gitService.getBranches(dashboardId);
|
||||
branches = await gitService.getBranches(dashboardId, envId);
|
||||
console.log(`[BranchSelector][Coherence:OK] Loaded ${branches.length} branches`);
|
||||
} catch (e) {
|
||||
console.error(`[BranchSelector][Coherence:Failed] ${e.message}`);
|
||||
@@ -87,7 +88,7 @@
|
||||
async function handleCheckout(branchName) {
|
||||
console.log(`[BranchSelector][Action] Checking out branch ${branchName}`);
|
||||
try {
|
||||
await gitService.checkoutBranch(dashboardId, branchName);
|
||||
await gitService.checkoutBranch(dashboardId, branchName, envId);
|
||||
currentBranch = branchName;
|
||||
dispatch('change', { branch: branchName });
|
||||
toast($t.git?.switched_to?.replace('{branch}', branchName), 'success');
|
||||
@@ -109,7 +110,7 @@
|
||||
if (!newBranchName) return;
|
||||
console.log(`[BranchSelector][Action] Creating branch ${newBranchName} from ${currentBranch}`);
|
||||
try {
|
||||
await gitService.createBranch(dashboardId, newBranchName, currentBranch);
|
||||
await gitService.createBranch(dashboardId, newBranchName, currentBranch, envId);
|
||||
toast($t.git?.created_branch?.replace('{branch}', newBranchName), 'success');
|
||||
showCreate = false;
|
||||
newBranchName = '';
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
// [SECTION: PROPS]
|
||||
let {
|
||||
dashboardId,
|
||||
envId = null,
|
||||
} = $props();
|
||||
|
||||
// [/SECTION]
|
||||
@@ -48,7 +49,7 @@
|
||||
console.log(`[CommitHistory][Action] Loading history for dashboard ${dashboardId}`);
|
||||
loading = true;
|
||||
try {
|
||||
history = await gitService.getHistory(dashboardId);
|
||||
history = await gitService.getHistory(dashboardId, 50, envId);
|
||||
console.log(`[CommitHistory][Coherence:OK] Loaded ${history.length} commits`);
|
||||
} catch (e) {
|
||||
console.error(`[CommitHistory][Coherence:Failed] ${e.message}`);
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
// [/SECTION]
|
||||
|
||||
// [SECTION: PROPS]
|
||||
let { dashboardId, show = false } = $props();
|
||||
let { dashboardId, envId = null, show = false } = $props();
|
||||
|
||||
// [/SECTION]
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
);
|
||||
// postApi returns the JSON data directly or throws an error
|
||||
const data = await api.postApi(
|
||||
`/git/repositories/${dashboardId}/generate-message`,
|
||||
`/git/repositories/${encodeURIComponent(String(dashboardId))}/generate-message${envId ? `?env_id=${encodeURIComponent(String(envId))}` : ""}`,
|
||||
);
|
||||
message = data.message;
|
||||
toast($t.git?.commit_message_generated, "success");
|
||||
@@ -72,17 +72,19 @@
|
||||
console.log(
|
||||
`[CommitModal][Action] Loading status and diff for ${dashboardId}`,
|
||||
);
|
||||
status = await gitService.getStatus(dashboardId);
|
||||
status = await gitService.getStatus(dashboardId, envId);
|
||||
// Fetch both unstaged and staged diffs to show complete picture
|
||||
const unstagedDiff = await gitService.getDiff(
|
||||
dashboardId,
|
||||
null,
|
||||
false,
|
||||
envId,
|
||||
);
|
||||
const stagedDiff = await gitService.getDiff(
|
||||
dashboardId,
|
||||
null,
|
||||
true,
|
||||
envId,
|
||||
);
|
||||
|
||||
diff = "";
|
||||
@@ -114,7 +116,7 @@
|
||||
);
|
||||
committing = true;
|
||||
try {
|
||||
await gitService.commit(dashboardId, message, []);
|
||||
await gitService.commit(dashboardId, message, [], envId);
|
||||
toast($t.git?.commit_success, "success");
|
||||
dispatch("commit");
|
||||
show = false;
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
// [/SECTION]
|
||||
|
||||
// [SECTION: PROPS]
|
||||
let { dashboardId, show = false } = $props();
|
||||
let { dashboardId, envId = null, show = false } = $props();
|
||||
|
||||
// [/SECTION]
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
console.log(`[DeploymentModal][Action] Deploying to ${selectedEnv}`);
|
||||
deploying = true;
|
||||
try {
|
||||
const result = await gitService.deploy(dashboardId, selectedEnv);
|
||||
const result = await gitService.deploy(dashboardId, selectedEnv, envId);
|
||||
toast(
|
||||
result.message || $t.git?.deploy_success,
|
||||
"success",
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
// [SECTION: PROPS]
|
||||
let {
|
||||
dashboardId,
|
||||
envId = null,
|
||||
dashboardTitle = "",
|
||||
show = false,
|
||||
} = $props();
|
||||
@@ -49,8 +50,19 @@
|
||||
let configs = $state([]);
|
||||
let selectedConfigId = $state("");
|
||||
let remoteUrl = $state("");
|
||||
let creatingRemoteRepo = $state(false);
|
||||
// [/SECTION]
|
||||
|
||||
// [DEF:isNumericDashboardRef:Function]
|
||||
/**
|
||||
* @purpose Checks whether current dashboard reference is numeric ID.
|
||||
* @post Returns true when dashboardId is digits-only.
|
||||
*/
|
||||
function isNumericDashboardRef() {
|
||||
return /^\d+$/.test(String(dashboardId || "").trim());
|
||||
}
|
||||
// [/DEF:isNumericDashboardRef:Function]
|
||||
|
||||
// [DEF:checkStatus:Function]
|
||||
/**
|
||||
* @purpose Проверяет, инициализирован ли репозиторий для данного дашборда.
|
||||
@@ -58,16 +70,23 @@
|
||||
* @post initialized state is set; configs loaded if not initialized.
|
||||
*/
|
||||
async function checkStatus() {
|
||||
if (isNumericDashboardRef()) {
|
||||
checkingStatus = false;
|
||||
initialized = false;
|
||||
toast('GitManager requires dashboard slug. Numeric ID is forbidden.', 'error');
|
||||
return;
|
||||
}
|
||||
checkingStatus = true;
|
||||
try {
|
||||
// If we can get branches, it means repo exists
|
||||
await gitService.getBranches(dashboardId);
|
||||
await gitService.getBranches(dashboardId, envId);
|
||||
initialized = true;
|
||||
} catch (e) {
|
||||
initialized = false;
|
||||
// Load configs if not initialized
|
||||
configs = await gitService.getConfigs();
|
||||
if (configs.length > 0) selectedConfigId = configs[0].id;
|
||||
const defaultConfig = resolveDefaultConfig(configs);
|
||||
if (defaultConfig?.id) selectedConfigId = defaultConfig.id;
|
||||
} finally {
|
||||
checkingStatus = false;
|
||||
}
|
||||
@@ -81,13 +100,17 @@
|
||||
* @post Repository is created on backend; initialized set to true.
|
||||
*/
|
||||
async function handleInit() {
|
||||
if (isNumericDashboardRef()) {
|
||||
toast('GitManager requires dashboard slug. Numeric ID is forbidden.', 'error');
|
||||
return;
|
||||
}
|
||||
if (!selectedConfigId || !remoteUrl) {
|
||||
toast($t.git?.init_validation_error, 'error');
|
||||
return;
|
||||
}
|
||||
loading = true;
|
||||
try {
|
||||
await gitService.initRepository(dashboardId, selectedConfigId, remoteUrl);
|
||||
await gitService.initRepository(dashboardId, selectedConfigId, remoteUrl, envId);
|
||||
toast($t.git?.init_success, 'success');
|
||||
initialized = true;
|
||||
} catch (e) {
|
||||
@@ -98,6 +121,91 @@
|
||||
}
|
||||
// [/DEF:handleInit:Function]
|
||||
|
||||
// [DEF:getSelectedConfig:Function]
|
||||
/**
|
||||
* @purpose Returns currently selected Git server config.
|
||||
* @post Config object or null.
|
||||
*/
|
||||
function getSelectedConfig() {
|
||||
return configs.find((item) => item.id === selectedConfigId) || null;
|
||||
}
|
||||
// [/DEF:getSelectedConfig:Function]
|
||||
|
||||
// [DEF:resolveDefaultConfig:Function]
|
||||
/**
|
||||
* @purpose Resolves default Git config for current dashboard session.
|
||||
* @post Returns config by priority: selected -> is_default -> CONNECTED -> first.
|
||||
*/
|
||||
function resolveDefaultConfig(configList) {
|
||||
if (!Array.isArray(configList) || configList.length === 0) return null;
|
||||
const selected = configList.find((item) => item.id === selectedConfigId);
|
||||
if (selected) return selected;
|
||||
const explicitDefault = configList.find((item) => item?.is_default);
|
||||
if (explicitDefault) return explicitDefault;
|
||||
const connected = configList.find((item) => item?.status === 'CONNECTED');
|
||||
return connected || configList[0];
|
||||
}
|
||||
// [/DEF:resolveDefaultConfig:Function]
|
||||
|
||||
// [DEF:buildSuggestedRepoName:Function]
|
||||
/**
|
||||
* @purpose Builds deterministic repository name from dashboard title/id.
|
||||
* @post Returns kebab-case name.
|
||||
*/
|
||||
function buildSuggestedRepoName() {
|
||||
const source = (dashboardTitle || `dashboard-${dashboardId}`).toLowerCase();
|
||||
const slug = source
|
||||
.replace(/[^a-z0-9]+/g, '-')
|
||||
.replace(/^-+|-+$/g, '')
|
||||
.slice(0, 48);
|
||||
return `${slug || 'dashboard'}-${dashboardId}`;
|
||||
}
|
||||
// [/DEF:buildSuggestedRepoName:Function]
|
||||
|
||||
// [DEF:handleCreateRemoteRepo:Function]
|
||||
/**
|
||||
* @purpose Creates remote repository on selected Git server and fills remote URL.
|
||||
* @pre selectedConfigId is selected.
|
||||
* @post remoteUrl is set from created repository clone URL.
|
||||
*/
|
||||
async function handleCreateRemoteRepo() {
|
||||
const config = getSelectedConfig() || resolveDefaultConfig(configs);
|
||||
if (!config) {
|
||||
toast($t.git?.init_validation_error || 'Select Git server first', 'error');
|
||||
return;
|
||||
}
|
||||
if (!selectedConfigId && config.id) selectedConfigId = config.id;
|
||||
const suggestedName = buildSuggestedRepoName();
|
||||
const inputName = prompt(
|
||||
`Repository name for ${config.provider}:`,
|
||||
suggestedName,
|
||||
);
|
||||
const repoName = String(inputName || '').trim();
|
||||
if (!repoName) return;
|
||||
|
||||
creatingRemoteRepo = true;
|
||||
try {
|
||||
const repo = await gitService.createRemoteRepository(config.id, {
|
||||
name: repoName,
|
||||
private: true,
|
||||
description: `Superset dashboard ${dashboardId}: ${dashboardTitle || repoName}`,
|
||||
auto_init: true,
|
||||
default_branch: 'main',
|
||||
});
|
||||
const resolvedRemoteUrl = repo?.clone_url || repo?.html_url || '';
|
||||
if (!resolvedRemoteUrl) {
|
||||
throw new Error('Remote repository created, but URL is empty');
|
||||
}
|
||||
remoteUrl = resolvedRemoteUrl;
|
||||
toast(`Repository created on ${config.provider}`, 'success');
|
||||
} catch (e) {
|
||||
toast(e.message, 'error');
|
||||
} finally {
|
||||
creatingRemoteRepo = false;
|
||||
}
|
||||
}
|
||||
// [/DEF:handleCreateRemoteRepo:Function]
|
||||
|
||||
// [DEF:handleSync:Function]
|
||||
/**
|
||||
* @purpose Синхронизирует состояние Superset с локальным Git-репозиторием.
|
||||
@@ -105,11 +213,15 @@
|
||||
* @post Dashboard YAMLs are exported to Git and staged.
|
||||
*/
|
||||
async function handleSync() {
|
||||
if (isNumericDashboardRef()) {
|
||||
toast('GitManager requires dashboard slug. Numeric ID is forbidden.', 'error');
|
||||
return;
|
||||
}
|
||||
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);
|
||||
await gitService.sync(dashboardId, sourceEnvId, envId);
|
||||
toast($t.git?.sync_success, 'success');
|
||||
} catch (e) {
|
||||
toast(e.message, 'error');
|
||||
@@ -126,9 +238,13 @@
|
||||
* @post Changes are pushed to origin.
|
||||
*/
|
||||
async function handlePush() {
|
||||
if (isNumericDashboardRef()) {
|
||||
toast('GitManager requires dashboard slug. Numeric ID is forbidden.', 'error');
|
||||
return;
|
||||
}
|
||||
loading = true;
|
||||
try {
|
||||
await gitService.push(dashboardId);
|
||||
await gitService.push(dashboardId, envId);
|
||||
toast($t.git?.push_success, 'success');
|
||||
} catch (e) {
|
||||
toast(e.message, 'error');
|
||||
@@ -145,9 +261,13 @@
|
||||
* @post Local branch is updated with remote changes.
|
||||
*/
|
||||
async function handlePull() {
|
||||
if (isNumericDashboardRef()) {
|
||||
toast('GitManager requires dashboard slug. Numeric ID is forbidden.', 'error');
|
||||
return;
|
||||
}
|
||||
loading = true;
|
||||
try {
|
||||
await gitService.pull(dashboardId);
|
||||
await gitService.pull(dashboardId, envId);
|
||||
toast($t.git?.pull_success, 'success');
|
||||
} catch (e) {
|
||||
toast(e.message, 'error');
|
||||
@@ -240,10 +360,19 @@
|
||||
bind:value={remoteUrl}
|
||||
placeholder={$t.git?.remote_url_placeholder}
|
||||
/>
|
||||
<Button
|
||||
variant="secondary"
|
||||
onclick={handleCreateRemoteRepo}
|
||||
disabled={creatingRemoteRepo || configs.length === 0 || !selectedConfigId}
|
||||
isLoading={creatingRemoteRepo}
|
||||
class="w-full"
|
||||
>
|
||||
Create repo
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
onclick={handleInit}
|
||||
disabled={loading || configs.length === 0}
|
||||
disabled={loading || configs.length === 0 || creatingRemoteRepo}
|
||||
isLoading={loading}
|
||||
class="w-full"
|
||||
>
|
||||
@@ -258,7 +387,7 @@
|
||||
<div class="md:col-span-1 space-y-6">
|
||||
<section>
|
||||
<h3 class="text-sm font-semibold text-gray-400 uppercase tracking-wider mb-3">{$t.git.branch}</h3>
|
||||
<BranchSelector {dashboardId} bind:currentBranch />
|
||||
<BranchSelector {dashboardId} {envId} bind:currentBranch />
|
||||
</section>
|
||||
|
||||
<section class="space-y-3">
|
||||
@@ -313,7 +442,7 @@
|
||||
|
||||
<!-- Right Column: History -->
|
||||
<div class="md:col-span-2 border-l pl-6">
|
||||
<CommitHistory {dashboardId} />
|
||||
<CommitHistory {dashboardId} {envId} />
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
@@ -323,12 +452,14 @@
|
||||
|
||||
<CommitModal
|
||||
{dashboardId}
|
||||
{envId}
|
||||
bind:show={showCommitModal}
|
||||
on:commit={() => { /* Refresh history */ }}
|
||||
/>
|
||||
|
||||
<DeploymentModal
|
||||
{dashboardId}
|
||||
{envId}
|
||||
bind:show={showDeployModal}
|
||||
/>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user