aboutsummaryrefslogtreecommitdiffstats
path: root/apps/settings/src/components
diff options
context:
space:
mode:
Diffstat (limited to 'apps/settings/src/components')
-rw-r--r--apps/settings/src/components/AdminAI.vue22
-rw-r--r--apps/settings/src/components/AppAPI/DaemonSelectionDialog.vue41
-rw-r--r--apps/settings/src/components/AppAPI/DaemonSelectionEntry.vue77
-rw-r--r--apps/settings/src/components/AppAPI/DaemonSelectionList.vue77
-rw-r--r--apps/settings/src/components/AppList/AppItem.vue26
-rw-r--r--apps/settings/src/components/AppStoreSidebar/AppDeployOptionsModal.vue12
-rw-r--r--apps/settings/src/components/AppStoreSidebar/AppDetailsTab.vue36
7 files changed, 281 insertions, 10 deletions
diff --git a/apps/settings/src/components/AdminAI.vue b/apps/settings/src/components/AdminAI.vue
index 044ebd9183e..0d3e9154bb9 100644
--- a/apps/settings/src/components/AdminAI.vue
+++ b/apps/settings/src/components/AdminAI.vue
@@ -11,16 +11,17 @@
@update:modelValue="saveChanges">
{{ t('settings', 'Allow AI usage for guest users') }}
</NcCheckboxRadioSwitch>
+ <h3>{{ t('settings', 'Provider for Task types') }}</h3>
<template v-for="type in taskProcessingTaskTypes">
- <div :key="type">
- <h3>{{ t('settings', 'Task:') }} {{ type.name }}</h3>
- <p>{{ type.description }}</p>
+ <div :key="type" class="tasktype-item">
+ <p class="tasktype-name">
+ {{ type.name }}
+ </p>
<NcCheckboxRadioSwitch v-model="settings['ai.taskprocessing_type_preferences'][type.id]"
type="switch"
@update:modelValue="saveChanges">
{{ t('settings', 'Enable') }}
- </NcCheckboxRadioSwitch>
- <NcSelect v-model="settings['ai.taskprocessing_provider_preferences'][type.id]"
+ </NcCheckboxRadioSwitch><NcSelect v-model="settings['ai.taskprocessing_provider_preferences'][type.id]"
class="provider-select"
:clearable="false"
:disabled="!settings['ai.taskprocessing_type_preferences'][type.id]"
@@ -33,7 +34,6 @@
{{ taskProcessingProviders.find(p => p.id === label)?.name }}
</template>
</NcSelect>
- <p>&nbsp;</p>
</div>
</template>
<template v-if="!hasTaskProcessing">
@@ -244,4 +244,14 @@ export default {
.provider-select {
min-width: 350px !important;
}
+
+.tasktype-item {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ .tasktype-name {
+ flex: 1;
+ margin: 0;
+ }
+}
</style>
diff --git a/apps/settings/src/components/AppAPI/DaemonSelectionDialog.vue b/apps/settings/src/components/AppAPI/DaemonSelectionDialog.vue
new file mode 100644
index 00000000000..696c77d19ce
--- /dev/null
+++ b/apps/settings/src/components/AppAPI/DaemonSelectionDialog.vue
@@ -0,0 +1,41 @@
+<!--
+ - SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
+ - SPDX-License-Identifier: AGPL-3.0-or-later
+-->
+<template>
+ <NcDialog :open="show"
+ :name="t('settings', 'Choose Deploy Daemon for {appName}', {appName: app.name })"
+ size="normal"
+ @update:open="closeModal">
+ <DaemonSelectionList :app="app"
+ :deploy-options="deployOptions"
+ @close="closeModal" />
+ </NcDialog>
+</template>
+
+<script setup>
+import { defineProps, defineEmits } from 'vue'
+import NcDialog from '@nextcloud/vue/components/NcDialog'
+import DaemonSelectionList from './DaemonSelectionList.vue'
+
+defineProps({
+ show: {
+ type: Boolean,
+ required: true,
+ },
+ app: {
+ type: Object,
+ required: true,
+ },
+ deployOptions: {
+ type: Object,
+ required: false,
+ default: () => ({}),
+ },
+})
+
+const emit = defineEmits(['update:show'])
+const closeModal = () => {
+ emit('update:show', false)
+}
+</script>
diff --git a/apps/settings/src/components/AppAPI/DaemonSelectionEntry.vue b/apps/settings/src/components/AppAPI/DaemonSelectionEntry.vue
new file mode 100644
index 00000000000..6b1cefde032
--- /dev/null
+++ b/apps/settings/src/components/AppAPI/DaemonSelectionEntry.vue
@@ -0,0 +1,77 @@
+<!--
+ - SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
+ - SPDX-License-Identifier: AGPL-3.0-or-later
+-->
+<template>
+ <NcListItem :name="itemTitle"
+ :details="isDefault ? t('settings', 'Default') : ''"
+ :force-display-actions="true"
+ :counter-number="daemon.exAppsCount"
+ :active="isDefault"
+ counter-type="highlighted"
+ @click.stop="selectDaemonAndInstall">
+ <template #subname>
+ {{ daemon.accepts_deploy_id }}
+ </template>
+ </NcListItem>
+</template>
+
+<script>
+import NcListItem from '@nextcloud/vue/components/NcListItem'
+import AppManagement from '../../mixins/AppManagement.js'
+import { useAppsStore } from '../../store/apps-store'
+import { useAppApiStore } from '../../store/app-api-store'
+
+export default {
+ name: 'DaemonSelectionEntry',
+ components: {
+ NcListItem,
+ },
+ mixins: [AppManagement], // TODO: Convert to Composition API when AppManagement is refactored
+ props: {
+ daemon: {
+ type: Object,
+ required: true,
+ },
+ isDefault: {
+ type: Boolean,
+ required: true,
+ },
+ app: {
+ type: Object,
+ required: true,
+ },
+ deployOptions: {
+ type: Object,
+ required: false,
+ default: () => ({}),
+ },
+ },
+ setup() {
+ const store = useAppsStore()
+ const appApiStore = useAppApiStore()
+
+ return {
+ store,
+ appApiStore,
+ }
+ },
+ computed: {
+ itemTitle() {
+ return this.daemon.name + ' - ' + this.daemon.display_name
+ },
+ daemons() {
+ return this.appApiStore.dockerDaemons
+ },
+ },
+ methods: {
+ closeModal() {
+ this.$emit('close')
+ },
+ selectDaemonAndInstall() {
+ this.closeModal()
+ this.enable(this.app.id, this.daemon, this.deployOptions)
+ },
+ },
+}
+</script>
diff --git a/apps/settings/src/components/AppAPI/DaemonSelectionList.vue b/apps/settings/src/components/AppAPI/DaemonSelectionList.vue
new file mode 100644
index 00000000000..701a17dbe24
--- /dev/null
+++ b/apps/settings/src/components/AppAPI/DaemonSelectionList.vue
@@ -0,0 +1,77 @@
+<!--
+ - SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
+ - SPDX-License-Identifier: AGPL-3.0-or-later
+-->
+<template>
+ <div class="daemon-selection-list">
+ <ul v-if="dockerDaemons.length > 0"
+ :aria-label="t('settings', 'Registered Deploy daemons list')">
+ <DaemonSelectionEntry v-for="daemon in dockerDaemons"
+ :key="daemon.id"
+ :daemon="daemon"
+ :is-default="defaultDaemon.name === daemon.name"
+ :app="app"
+ :deploy-options="deployOptions"
+ @close="closeModal" />
+ </ul>
+ <NcEmptyContent v-else
+ class="daemon-selection-list__empty-content"
+ :name="t('settings', 'No Deploy daemons configured')"
+ :description="t('settings', 'Register a custom one or setup from available templates')">
+ <template #icon>
+ <FormatListBullet :size="20" />
+ </template>
+ <template #action>
+ <NcButton :href="appApiAdminPage">
+ {{ t('settings', 'Manage Deploy daemons') }}
+ </NcButton>
+ </template>
+ </NcEmptyContent>
+ </div>
+</template>
+
+<script setup>
+import { computed, defineProps } from 'vue'
+import { generateUrl } from '@nextcloud/router'
+
+import NcEmptyContent from '@nextcloud/vue/components/NcEmptyContent'
+import NcButton from '@nextcloud/vue/components/NcButton'
+import FormatListBullet from 'vue-material-design-icons/FormatListBulleted.vue'
+import DaemonSelectionEntry from './DaemonSelectionEntry.vue'
+import { useAppApiStore } from '../../store/app-api-store.ts'
+
+defineProps({
+ app: {
+ type: Object,
+ required: true,
+ },
+ deployOptions: {
+ type: Object,
+ required: false,
+ default: () => ({}),
+ },
+})
+
+const appApiStore = useAppApiStore()
+
+const dockerDaemons = computed(() => appApiStore.dockerDaemons)
+const defaultDaemon = computed(() => appApiStore.defaultDaemon)
+const appApiAdminPage = computed(() => generateUrl('/settings/admin/app_api'))
+const emit = defineEmits(['close'])
+const closeModal = () => {
+ emit('close')
+}
+</script>
+
+<style scoped lang="scss">
+.daemon-selection-list {
+ max-height: 350px;
+ overflow-y: scroll;
+ padding: 2rem;
+
+ &__empty-content {
+ margin-top: 0;
+ text-align: center;
+ }
+}
+</style>
diff --git a/apps/settings/src/components/AppList/AppItem.vue b/apps/settings/src/components/AppList/AppItem.vue
index d0f39f3c74a..95a98a93cde 100644
--- a/apps/settings/src/components/AppList/AppItem.vue
+++ b/apps/settings/src/components/AppList/AppItem.vue
@@ -100,7 +100,7 @@
:aria-label="enableButtonTooltip"
type="primary"
:disabled="!app.canInstall || installing || isLoading || !defaultDeployDaemonAccessible || isInitializing || isDeploying"
- @click.stop="enable(app.id)">
+ @click.stop="enableButtonAction">
{{ enableButtonText }}
</NcButton>
<NcButton v-else-if="!app.active"
@@ -111,6 +111,10 @@
@click.stop="forceEnable(app.id)">
{{ forceEnableButtonText }}
</NcButton>
+
+ <DaemonSelectionDialog v-if="app?.app_api && showSelectDaemonModal"
+ :show.sync="showSelectDaemonModal"
+ :app="app" />
</component>
</component>
</template>
@@ -126,6 +130,7 @@ import NcButton from '@nextcloud/vue/components/NcButton'
import NcIconSvgWrapper from '@nextcloud/vue/components/NcIconSvgWrapper'
import { mdiCogOutline } from '@mdi/js'
import { useAppApiStore } from '../../store/app-api-store.ts'
+import DaemonSelectionDialog from '../AppAPI/DaemonSelectionDialog.vue'
export default {
name: 'AppItem',
@@ -134,6 +139,7 @@ export default {
AppScore,
NcButton,
NcIconSvgWrapper,
+ DaemonSelectionDialog,
},
mixins: [AppManagement, SvgFilterMixin],
props: {
@@ -177,6 +183,7 @@ export default {
isSelected: false,
scrolled: false,
screenshotLoaded: false,
+ showSelectDaemonModal: false,
}
},
computed: {
@@ -219,6 +226,23 @@ export default {
getDataItemHeaders(columnName) {
return this.useBundleView ? [this.headers, columnName].join(' ') : null
},
+ showSelectionModal() {
+ this.showSelectDaemonModal = true
+ },
+ async enableButtonAction() {
+ if (!this.app?.app_api) {
+ this.enable(this.app.id)
+ return
+ }
+ await this.appApiStore.fetchDockerDaemons()
+ if (this.appApiStore.dockerDaemons.length === 1 && this.app.needsDownload) {
+ this.enable(this.app.id, this.appApiStore.dockerDaemons[0])
+ } else if (this.app.needsDownload) {
+ this.showSelectionModal()
+ } else {
+ this.enable(this.app.id, this.app.daemon)
+ }
+ },
},
}
</script>
diff --git a/apps/settings/src/components/AppStoreSidebar/AppDeployOptionsModal.vue b/apps/settings/src/components/AppStoreSidebar/AppDeployOptionsModal.vue
index 67d4afa6566..0544c3848be 100644
--- a/apps/settings/src/components/AppStoreSidebar/AppDeployOptionsModal.vue
+++ b/apps/settings/src/components/AppStoreSidebar/AppDeployOptionsModal.vue
@@ -152,6 +152,7 @@ import { computed, ref } from 'vue'
import axios from '@nextcloud/axios'
import { generateUrl } from '@nextcloud/router'
import { loadState } from '@nextcloud/initial-state'
+import { emit } from '@nextcloud/event-bus'
import NcDialog from '@nextcloud/vue/components/NcDialog'
import NcTextField from '@nextcloud/vue/components/NcTextField'
@@ -277,8 +278,15 @@ export default {
this.configuredDeployOptions = null
})
},
- submitDeployOptions() {
- this.enable(this.app.id, this.deployOptions)
+ async submitDeployOptions() {
+ await this.appApiStore.fetchDockerDaemons()
+ if (this.appApiStore.dockerDaemons.length === 1 && this.app.needsDownload) {
+ this.enable(this.app.id, this.appApiStore.dockerDaemons[0], this.deployOptions)
+ } else if (this.app.needsDownload) {
+ emit('showDaemonSelectionModal', this.deployOptions)
+ } else {
+ this.enable(this.app.id, this.app.daemon, this.deployOptions)
+ }
this.$emit('update:show', false)
},
},
diff --git a/apps/settings/src/components/AppStoreSidebar/AppDetailsTab.vue b/apps/settings/src/components/AppStoreSidebar/AppDetailsTab.vue
index 8a387b55ecf..eb66d8f3e3a 100644
--- a/apps/settings/src/components/AppStoreSidebar/AppDetailsTab.vue
+++ b/apps/settings/src/components/AppStoreSidebar/AppDetailsTab.vue
@@ -68,7 +68,7 @@
type="button"
:value="enableButtonText"
:disabled="!app.canInstall || installing || isLoading || !defaultDeployDaemonAccessible || isInitializing || isDeploying"
- @click="enable(app.id)">
+ @click="enableButtonAction">
<input v-else-if="!app.active && !app.canInstall"
:title="forceEnableButtonTooltip"
:aria-label="forceEnableButtonTooltip"
@@ -195,11 +195,16 @@
<AppDeployOptionsModal v-if="app?.app_api"
:show.sync="showDeployOptionsModal"
:app="app" />
+ <DaemonSelectionDialog v-if="app?.app_api"
+ :show.sync="showSelectDaemonModal"
+ :app="app"
+ :deploy-options="deployOptions" />
</div>
</NcAppSidebarTab>
</template>
<script>
+import { subscribe, unsubscribe } from '@nextcloud/event-bus'
import NcAppSidebarTab from '@nextcloud/vue/components/NcAppSidebarTab'
import NcButton from '@nextcloud/vue/components/NcButton'
import NcDateTime from '@nextcloud/vue/components/NcDateTime'
@@ -207,6 +212,7 @@ import NcIconSvgWrapper from '@nextcloud/vue/components/NcIconSvgWrapper'
import NcSelect from '@nextcloud/vue/components/NcSelect'
import NcCheckboxRadioSwitch from '@nextcloud/vue/components/NcCheckboxRadioSwitch'
import AppDeployOptionsModal from './AppDeployOptionsModal.vue'
+import DaemonSelectionDialog from '../AppAPI/DaemonSelectionDialog.vue'
import AppManagement from '../../mixins/AppManagement.js'
import { mdiBugOutline, mdiFeatureSearchOutline, mdiStar, mdiTextBoxOutline, mdiTooltipQuestionOutline, mdiToyBrickPlusOutline } from '@mdi/js'
@@ -224,6 +230,7 @@ export default {
NcSelect,
NcCheckboxRadioSwitch,
AppDeployOptionsModal,
+ DaemonSelectionDialog,
},
mixins: [AppManagement],
@@ -256,6 +263,8 @@ export default {
groupCheckedAppsData: false,
removeData: false,
showDeployOptionsModal: false,
+ showSelectDaemonModal: false,
+ deployOptions: null,
}
},
@@ -365,15 +374,40 @@ export default {
this.removeData = false
},
},
+ beforeUnmount() {
+ this.deployOptions = null
+ unsubscribe('showDaemonSelectionModal')
+ },
mounted() {
if (this.app.groups.length > 0) {
this.groupCheckedAppsData = true
}
+ subscribe('showDaemonSelectionModal', (deployOptions) => {
+ this.showSelectionModal(deployOptions)
+ })
},
methods: {
toggleRemoveData() {
this.removeData = !this.removeData
},
+ showSelectionModal(deployOptions = null) {
+ this.deployOptions = deployOptions
+ this.showSelectDaemonModal = true
+ },
+ async enableButtonAction() {
+ if (!this.app?.app_api) {
+ this.enable(this.app.id)
+ return
+ }
+ await this.appApiStore.fetchDockerDaemons()
+ if (this.appApiStore.dockerDaemons.length === 1 && this.app.needsDownload) {
+ this.enable(this.app.id, this.appApiStore.dockerDaemons[0])
+ } else if (this.app.needsDownload) {
+ this.showSelectionModal()
+ } else {
+ this.enable(this.app.id, this.app.daemon)
+ }
+ },
},
}
</script>