181 lines
6.8 KiB
Svelte
181 lines
6.8 KiB
Svelte
<!-- [DEF:DeploymentModal:Component] -->
|
|
<!--
|
|
@SEMANTICS: deployment, git, environment, modal
|
|
@PURPOSE: Modal for deploying a dashboard to a target environment.
|
|
@LAYER: Component
|
|
@RELATION: CALLS -> frontend/src/services/gitService.js
|
|
@RELATION: DISPATCHES -> deploy
|
|
|
|
@INVARIANT: Cannot deploy without a selected environment.
|
|
-->
|
|
|
|
<script>
|
|
// [SECTION: IMPORTS]
|
|
import { onMount, createEventDispatcher } from "svelte";
|
|
import { gitService } from "../../services/gitService";
|
|
import { addToast as toast } from "../../lib/toasts.js";
|
|
import { t } from "../../lib/i18n";
|
|
// [/SECTION]
|
|
|
|
// [SECTION: PROPS]
|
|
let { dashboardId, show = false } = $props();
|
|
|
|
// [/SECTION]
|
|
|
|
// [SECTION: STATE]
|
|
let environments = $state([]);
|
|
let selectedEnv = $state("");
|
|
let loading = $state(false);
|
|
let deploying = $state(false);
|
|
// [/SECTION]
|
|
|
|
const dispatch = createEventDispatcher();
|
|
|
|
// [DEF:loadStatus:Watcher]
|
|
$effect(() => {
|
|
if (show) loadEnvironments();
|
|
});
|
|
// [/DEF:loadStatus:Watcher]
|
|
|
|
// [DEF:loadEnvironments:Function]
|
|
/**
|
|
* @purpose Fetch available environments from API.
|
|
* @post environments state is populated.
|
|
* @side_effect Updates environments state.
|
|
*/
|
|
async function loadEnvironments() {
|
|
console.log(`[DeploymentModal][Action] Loading environments`);
|
|
loading = true;
|
|
try {
|
|
environments = await gitService.getEnvironments();
|
|
if (environments.length > 0) {
|
|
selectedEnv = environments[0].id;
|
|
}
|
|
console.log(
|
|
`[DeploymentModal][Coherence:OK] Loaded ${environments.length} environments`,
|
|
);
|
|
} catch (e) {
|
|
console.error(`[DeploymentModal][Coherence:Failed] ${e.message}`);
|
|
toast($t.migration?.loading_envs_failed || "Failed to load environments", "error");
|
|
} finally {
|
|
loading = false;
|
|
}
|
|
}
|
|
// [/DEF:loadEnvironments:Function]
|
|
|
|
// [DEF:handleDeploy:Function]
|
|
/**
|
|
* @purpose Trigger deployment to selected environment.
|
|
* @pre selectedEnv must be set.
|
|
* @post deploy event dispatched on success.
|
|
* @side_effect Triggers API call, closes modal, shows toast.
|
|
*/
|
|
async function handleDeploy() {
|
|
if (!selectedEnv) return;
|
|
console.log(`[DeploymentModal][Action] Deploying to ${selectedEnv}`);
|
|
deploying = true;
|
|
try {
|
|
const result = await gitService.deploy(dashboardId, selectedEnv);
|
|
toast(
|
|
result.message || ($t.git?.deploy_success || "Deployment triggered successfully"),
|
|
"success",
|
|
);
|
|
dispatch("deploy");
|
|
show = false;
|
|
console.log(`[DeploymentModal][Coherence:OK] Deployment triggered`);
|
|
} catch (e) {
|
|
console.error(`[DeploymentModal][Coherence:Failed] ${e.message}`);
|
|
toast(e.message, "error");
|
|
} finally {
|
|
deploying = false;
|
|
}
|
|
}
|
|
// [/DEF:handleDeploy:Function]
|
|
</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-xl w-96">
|
|
<h2 class="text-xl font-bold mb-4">{$t.git?.deploy || "Deploy Dashboard"}</h2>
|
|
|
|
{#if loading}
|
|
<p class="text-gray-500">{$t.migration?.loading_envs || "Loading environments..."}</p>
|
|
{:else if environments.length === 0}
|
|
<p class="text-red-500 mb-4">
|
|
{$t.git?.no_deploy_envs || "No deployment environments configured."}
|
|
</p>
|
|
<div class="flex justify-end">
|
|
<button
|
|
onclick={() => (show = false)}
|
|
class="px-4 py-2 bg-gray-200 text-gray-800 rounded hover:bg-gray-300"
|
|
>
|
|
{$t.common?.close || "Close"}
|
|
</button>
|
|
</div>
|
|
{:else}
|
|
<div class="mb-6">
|
|
<label class="block text-sm font-medium text-gray-700 mb-2"
|
|
>{$t.migration?.target_env || "Select Target Environment"}</label
|
|
>
|
|
<select
|
|
bind:value={selectedEnv}
|
|
class="w-full border rounded p-2 focus:ring-2 focus:ring-blue-500 outline-none bg-white"
|
|
>
|
|
{#each environments as env}
|
|
<option value={env.id}
|
|
>{env.name} ({env.superset_url})</option
|
|
>
|
|
{/each}
|
|
</select>
|
|
</div>
|
|
|
|
<div class="flex justify-end space-x-3">
|
|
<button
|
|
onclick={() => (show = false)}
|
|
class="px-4 py-2 text-gray-600 hover:bg-gray-100 rounded"
|
|
>
|
|
{$t.common?.cancel || "Cancel"}
|
|
</button>
|
|
<button
|
|
onclick={handleDeploy}
|
|
disabled={deploying || !selectedEnv}
|
|
class="px-4 py-2 bg-green-600 text-white rounded hover:bg-green-700 disabled:opacity-50 flex items-center"
|
|
>
|
|
{#if deploying}
|
|
<svg
|
|
class="animate-spin -ml-1 mr-2 h-4 w-4 text-white"
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
fill="none"
|
|
viewBox="0 0 24 24"
|
|
>
|
|
<circle
|
|
class="opacity-25"
|
|
cx="12"
|
|
cy="12"
|
|
r="10"
|
|
stroke="currentColor"
|
|
stroke-width="4"
|
|
></circle>
|
|
<path
|
|
class="opacity-75"
|
|
fill="currentColor"
|
|
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
|
></path>
|
|
</svg>
|
|
{$t.git?.deploying || "Deploying..."}
|
|
{:else}
|
|
{$t.git?.deploy || "Deploy"}
|
|
{/if}
|
|
</button>
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
</div>
|
|
{/if}
|
|
<!-- [/SECTION] -->
|
|
|
|
<!-- [/DEF:DeploymentModal:Component] -->
|