app install: Fix status of install button

This commit is contained in:
Girish Ramakrishnan
2026-01-20 17:00:02 +01:00
parent e34cf8f6a6
commit 11a8a73723
2 changed files with 35 additions and 24 deletions

View File

@@ -1,9 +1,9 @@
<script setup>
import { ref, computed, useTemplateRef, onMounted, inject } from 'vue';
import { ref, computed, useTemplateRef, onMounted, inject, watch } from 'vue';
import { marked } from 'marked';
import { Button, Dialog, SingleSelect, FormGroup, TextInput, InputGroup, Spinner } from '@cloudron/pankow';
import { prettyDate, prettyBinarySize, isValidDomain } from '@cloudron/pankow/utils';
import { prettyDate, prettyBinarySize } from '@cloudron/pankow/utils';
import AccessControl from './AccessControl.vue';
import PortBindings from './PortBindings.vue';
import AppsModel from '../models/AppsModel.js';
@@ -39,23 +39,28 @@ const locationInput = useTemplateRef('locationInput');
const description = computed(() => marked.parse(manifest.value.description || ''));
const domains = ref([]);
const formValid = computed(() => {
if (!domain.value) return false;
const form = ref(null); // assigned via "Function Ref" because it is inside v-if
const isFormValid = ref(false);
async function checkValidity() {
isFormValid.value = form.value ? form.value.checkValidity() : false;
if (location.value && !isValidDomain(location.value + '.' + domain.value)) return false;
if (isFormValid.value) {
if (accessRestrictionOption.value === ACL_OPTIONS.RESTRICTED && (accessRestrictionAcl.value.users.length === 0 && accessRestrictionAcl.value.groups.length === 0)) {
isFormValid.value = true;
}
if (accessRestrictionOption.value === ACL_OPTIONS.RESTRICTED && (accessRestrictionAcl.value.users.length === 0 && accessRestrictionAcl.value.groups.length === 0)) return false;
if (manifest.value.id === PROXY_APP_ID) {
try {
new URL(upstreamUri.value);
// eslint-disable-next-line no-unused-vars
} catch (e) {
return false;
if (manifest.value.id === PROXY_APP_ID) {
try {
new URL(upstreamUri.value);
// eslint-disable-next-line no-unused-vars
} catch (e) {
isFormValid.value = false;
}
}
}
return true;
}
watch(form, () => { // trigger form validation when the ref becomes set
setTimeout(checkValidity, 100);
});
const appMaxCountExceeded = ref(false);
@@ -68,7 +73,9 @@ function setStep(newStep) {
}
step.value = newStep;
if (newStep === STEP.INSTALL) setTimeout(() => locationInput.value.$el.focus(), 500);
if (newStep === STEP.INSTALL) {
setTimeout(() => locationInput.value.$el.focus(), 500);
}
}
// form data
@@ -91,6 +98,8 @@ function onDomainChange() {
}
async function onSubmit(overwriteDns) {
if (!form.value.reportValidity()) return;
formError.value = {};
busy.value = true;
@@ -309,15 +318,15 @@ defineExpose({
<div class="error-label" v-if="formError.generic">{{ formError.generic }}</div>
<div class="error-label" v-if="formError.dnsExists">{{ formError.dnsExists }}</div>
<form @submit.prevent="onSubmit(false)" autocomplete="off">
<form :ref="(el) => { form = el; }" @submit.prevent="onSubmit(false)" autocomplete="off" @input="checkValidity()">
<fieldset :disabled="busy">
<input style="display: none;" type="submit" :disabled="!formValid" />
<input style="display: none;" type="submit" :disabled="busy" />
<FormGroup :class="{ 'has-error': formError.location }">
<label for="location">{{ $t('appstore.installDialog.location') }}</label>
<InputGroup>
<TextInput id="location" ref="locationInput" v-model="location" style="flex-grow: 1"/>
<SingleSelect v-model="domain" :options="domains" option-label="label" option-key="domain" @select="onDomainChange()" :search-threshold="10"/>
<SingleSelect v-model="domain" :options="domains" option-label="label" option-key="domain" @select="onDomainChange()" :search-threshold="10" required/>
</InputGroup>
<div class="warning-label" v-show="domainProvider === 'noop' || domainProvider === 'manual'" v-html="$t('appstore.installDialog.manualWarning', { location: ((location ? location + '.' : '') + domain) })"></div>
<div class="error-label" v-if="formError.location">{{ formError.location }}</div>
@@ -328,21 +337,21 @@ defineExpose({
<small>{{ port.description }}</small>
<InputGroup>
<TextInput :id="'secondaryDomainInput' + key" v-model="port.value" :placeholder="$t('appstore.installDialog.locationPlaceholder')" style="flex-grow: 1"/>
<SingleSelect v-model="port.domain" :options="domains" option-label="label" option-key="domain" />
<SingleSelect v-model="port.domain" :options="domains" option-label="label" option-key="domain" required/>
</InputGroup>
</FormGroup>
<FormGroup :class="{ 'has-error': formError.upstreamUri }" v-show="manifest.id === PROXY_APP_ID">
<FormGroup :class="{ 'has-error': formError.upstreamUri }" v-if="manifest.id === PROXY_APP_ID">
<label for="upstreamUri">Upstream URI</label>
<TextInput id="upstreamUri" v-model="upstreamUri" />
<TextInput id="upstreamUri" v-model="upstreamUri" required/>
</FormGroup>
<PortBindings v-model:tcp="tcpPorts" v-model:udp="udpPorts" :error="formError" :domain-provider="domainProvider"/>
<AccessControl v-model:option="accessRestrictionOption" v-model:acl="accessRestrictionAcl" :manifest="manifest" :users="users" :groups="groups" :installation="true"/>
<div class="bottom-button-bar">
<Button v-if="needsOverwriteDns" danger @click="onSubmit(true)" icon="fa-solid fa-circle-down" :disabled="!formValid" :loading="busy">Install {{ manifest.title }} and overwrite DNS</Button>
<Button v-else @click="onSubmit(false)" icon="fa-solid fa-circle-down" :disabled="!formValid" :loading="busy">Install {{ manifest.title }}</Button>
<Button v-if="needsOverwriteDns" danger @click="onSubmit(true)" icon="fa-solid fa-circle-down" :disabled="busy || !isFormValid" :loading="busy">Install {{ manifest.title }} and overwrite DNS</Button>
<Button v-else @click="onSubmit(false)" icon="fa-solid fa-circle-down" :disabled="busy || !isFormValid" :loading="busy">Install {{ manifest.title }}</Button>
</div>
</fieldset>
</form>

View File

@@ -82,6 +82,8 @@ function onRemoveRedirect(index) {
}
async function onSubmit() {
if (!form.value.reportValidity()) return;
busy.value = true;
errorMessage.value = '';
errorObject.value = {};