feat : add deploy modal with tag selection and result display
This commit is contained in:
@@ -2,6 +2,8 @@
|
|||||||
import type { Application, ApplicationWrite, Environment, EnvironmentWrite } from '~/services/dto/application'
|
import type { Application, ApplicationWrite, Environment, EnvironmentWrite } from '~/services/dto/application'
|
||||||
import { getApplication, updateApplication, deleteApplication } from '~/services/applications'
|
import { getApplication, updateApplication, deleteApplication } from '~/services/applications'
|
||||||
import { createEnvironment, updateEnvironment, deleteEnvironment, toggleMaintenance } from '~/services/environments'
|
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 { t } = useI18n()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
@@ -12,6 +14,15 @@ const application = ref<Application | null>(null)
|
|||||||
const loading = ref(true)
|
const loading = ref(true)
|
||||||
const pendingMaintenanceByEnvId = ref<Record<number, boolean>>({})
|
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
|
// App edit modal
|
||||||
const showAppModal = ref(false)
|
const showAppModal = ref(false)
|
||||||
const editForm = ref<ApplicationWrite>({ name: '', slug: '', registryImage: '', description: '', giteaUrl: '' })
|
const editForm = ref<ApplicationWrite>({ name: '', slug: '', registryImage: '', description: '', giteaUrl: '' })
|
||||||
@@ -131,6 +142,43 @@ function removeLogFile(index: number) {
|
|||||||
envForm.value.logFiles.splice(index, 1)
|
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(() =>
|
const envModalTitle = computed(() =>
|
||||||
editingEnvId.value ? t('environments.editButton') : t('environments.addButton')
|
editingEnvId.value ? t('environments.editButton') : t('environments.addButton')
|
||||||
)
|
)
|
||||||
@@ -236,6 +284,12 @@ onMounted(loadApplication)
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
|
<MalioButton
|
||||||
|
:label="t('environments.deploy.button')"
|
||||||
|
icon-name="mdi:rocket-launch-outline"
|
||||||
|
icon-position="left"
|
||||||
|
@click="openDeployModal(env)"
|
||||||
|
/>
|
||||||
<MalioButton
|
<MalioButton
|
||||||
:label="pendingMaintenanceByEnvId[env.id!]
|
:label="pendingMaintenanceByEnvId[env.id!]
|
||||||
? t('environments.maintenance.pending')
|
? t('environments.maintenance.pending')
|
||||||
@@ -383,5 +437,83 @@ onMounted(loadApplication)
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</AppModal>
|
</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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
Reference in New Issue
Block a user