feat : add deploy modal with tag selection and result display

This commit is contained in:
2026-04-06 15:25:56 +02:00
parent 046feab8ac
commit 5d2aba634e

View File

@@ -2,6 +2,8 @@
import type { Application, ApplicationWrite, Environment, EnvironmentWrite } from '~/services/dto/application'
import { getApplication, updateApplication, deleteApplication } from '~/services/applications'
import { createEnvironment, updateEnvironment, deleteEnvironment, toggleMaintenance } from '~/services/environments'
import type { DeployResult } from '~/services/dto/deploy'
import { getAvailableTags, deploy } from '~/services/deploy'
const { t } = useI18n()
const route = useRoute()
@@ -12,6 +14,15 @@ const application = ref<Application | null>(null)
const loading = ref(true)
const pendingMaintenanceByEnvId = ref<Record<number, boolean>>({})
// Deploy modal
const showDeployModal = ref(false)
const deployEnvId = ref<number | null>(null)
const deployTags = ref<string[]>([])
const selectedTag = ref('')
const loadingTags = ref(false)
const isDeploying = ref(false)
const deployResult = ref<DeployResult | null>(null)
// App edit modal
const showAppModal = ref(false)
const editForm = ref<ApplicationWrite>({ name: '', slug: '', registryImage: '', description: '', giteaUrl: '' })
@@ -131,6 +142,43 @@ function removeLogFile(index: number) {
envForm.value.logFiles.splice(index, 1)
}
async function openDeployModal(env: Environment) {
deployEnvId.value = env.id!
selectedTag.value = ''
deployResult.value = null
deployTags.value = []
showDeployModal.value = true
loadingTags.value = true
try {
const response = await getAvailableTags(slug)
deployTags.value = response.tags ?? []
if (deployTags.value.length > 0) {
selectedTag.value = deployTags.value[0]
}
} finally {
loadingTags.value = false
}
}
async function handleDeploy() {
if (!deployEnvId.value || !selectedTag.value) return
isDeploying.value = true
deployResult.value = null
try {
deployResult.value = await deploy(deployEnvId.value, selectedTag.value)
if (deployResult.value.success) {
await loadApplication()
}
} finally {
isDeploying.value = false
}
}
function closeDeployModal() {
showDeployModal.value = false
deployResult.value = null
}
const envModalTitle = computed(() =>
editingEnvId.value ? t('environments.editButton') : t('environments.addButton')
)
@@ -236,6 +284,12 @@ onMounted(loadApplication)
</a>
</div>
<div class="flex gap-2">
<MalioButton
:label="t('environments.deploy.button')"
icon-name="mdi:rocket-launch-outline"
icon-position="left"
@click="openDeployModal(env)"
/>
<MalioButton
:label="pendingMaintenanceByEnvId[env.id!]
? t('environments.maintenance.pending')
@@ -383,5 +437,83 @@ onMounted(loadApplication)
</div>
</form>
</AppModal>
<!-- Deploy modal -->
<AppModal
v-model="showDeployModal"
:submit-label="isDeploying ? t('environments.deploy.deploying') : t('environments.deploy.confirm')"
:cancel-label="t('applications.form.cancel')"
:loading="isDeploying"
max-width="xl"
@submit="handleDeploy"
@update:model-value="!$event && closeDeployModal()"
>
<template #title>{{ t('environments.deploy.title') }}</template>
<!-- Tag selection -->
<div v-if="!deployResult">
<div v-if="loadingTags" class="py-8 text-center text-neutral-400">
<Icon name="mdi:loading" size="24" class="animate-spin" />
<p class="mt-2 text-sm">{{ t('environments.deploy.loadingTags') }}</p>
</div>
<div v-else-if="deployTags.length === 0" class="py-8 text-center text-neutral-400">
<Icon name="mdi:package-variant" size="32" />
<p class="mt-2 text-sm">{{ t('environments.deploy.noTags') }}</p>
</div>
<div v-else>
<label class="mb-1 block text-sm font-medium text-neutral-700">
{{ t('environments.deploy.selectTag') }}
</label>
<select
v-model="selectedTag"
class="w-full rounded-md border border-neutral-300 px-3 py-2 text-sm focus:border-primary-500 focus:outline-none focus:ring-1 focus:ring-primary-500"
>
<option v-for="tag in deployTags" :key="tag" :value="tag">
{{ tag }}
</option>
</select>
</div>
</div>
<!-- Deploy result -->
<div v-else>
<div
class="mb-4 flex items-center gap-2 rounded-lg px-4 py-3"
:class="deployResult.success
? 'bg-green-50 text-green-700'
: 'bg-red-50 text-red-700'"
>
<Icon
:name="deployResult.success ? 'mdi:check-circle' : 'mdi:alert-circle'"
size="20"
/>
<span class="text-sm font-semibold">
{{ deployResult.success
? t('environments.deploy.success')
: t('environments.deploy.error')
}}
</span>
<span class="ml-auto text-xs font-mono">{{ deployResult.tag }}</span>
</div>
<div>
<p class="mb-2 text-xs font-semibold uppercase tracking-wider text-neutral-400">
{{ t('environments.deploy.output') }}
</p>
<pre class="max-h-80 overflow-auto rounded-lg bg-neutral-900 p-4 text-xs text-green-400 font-mono whitespace-pre-wrap">{{ deployResult.output }}</pre>
</div>
</div>
<!-- Override footer when showing result -->
<template v-if="deployResult" #footer>
<MalioButton
:label="t('applications.form.cancel')"
variant="tertiary"
@click="closeDeployModal"
/>
</template>
</AppModal>
</div>
</template>