mirror of
https://github.com/YunoHost/yunohost-admin.git
synced 2024-09-03 20:06:15 +02:00
[comp] add AppCatalogDetails modal to display app info before install
This commit is contained in:
parent
581a919044
commit
daf8e58ce0
3 changed files with 217 additions and 4 deletions
|
@ -54,6 +54,43 @@
|
||||||
"api_not_found": "Seems like the web-admin tried to query something that doesn't exist.",
|
"api_not_found": "Seems like the web-admin tried to query something that doesn't exist.",
|
||||||
"api_not_responding": "The YunoHost API is not responding. Maybe 'yunohost-api' is down or got restarted?",
|
"api_not_responding": "The YunoHost API is not responding. Maybe 'yunohost-api' is down or got restarted?",
|
||||||
"api_waiting": "Waiting for the server's response...",
|
"api_waiting": "Waiting for the server's response...",
|
||||||
|
"app": {
|
||||||
|
"antifeatures": "Antifeatures:",
|
||||||
|
"preview": {
|
||||||
|
"before_install": "Things to know before install",
|
||||||
|
"integration": {
|
||||||
|
"archs": "Supported architectures:",
|
||||||
|
"ldap": {
|
||||||
|
"false": "Does not use YunoHost accounts to login (LDAP)",
|
||||||
|
"null": "No information about LDAP integration",
|
||||||
|
"true": "Use YunoHost accounts to login (LDAP)"
|
||||||
|
},
|
||||||
|
"multi_instance": {
|
||||||
|
"false": "Can be installed only once",
|
||||||
|
"true": "Can be installed several times"
|
||||||
|
},
|
||||||
|
"resources": "Typical resource usage: {ram} RAM, {disk} disk",
|
||||||
|
"sso": {
|
||||||
|
"false": "Single sign-on is not available (SSO)",
|
||||||
|
"null": "No information about SSO integration",
|
||||||
|
"true": "Single sign-on is available (SSO)"
|
||||||
|
},
|
||||||
|
"title": "YunoHost integration"
|
||||||
|
},
|
||||||
|
"links": {
|
||||||
|
"admindoc": "Official documentation",
|
||||||
|
"code": "Upstream code repository",
|
||||||
|
"forum": "Topics about this app on YunoHost's forum",
|
||||||
|
"package": "YunoHost package repository",
|
||||||
|
"title": "Links",
|
||||||
|
"website": "Website"
|
||||||
|
},
|
||||||
|
"title": "App details",
|
||||||
|
"try_demo": "Try the demo",
|
||||||
|
"version": "Current version: {version}"
|
||||||
|
},
|
||||||
|
"potential_alternative_to": "Potential alternative to:"
|
||||||
|
},
|
||||||
"app_choose_category": "Choose a category",
|
"app_choose_category": "Choose a category",
|
||||||
"app_config_panel": "Config panel",
|
"app_config_panel": "Config panel",
|
||||||
"app_config_panel_label": "Configure this app",
|
"app_config_panel_label": "Configure this app",
|
||||||
|
|
|
@ -106,10 +106,15 @@
|
||||||
</b-card>
|
</b-card>
|
||||||
</card-deck-feed>
|
</card-deck-feed>
|
||||||
|
|
||||||
<b-modal
|
<app-catalog-details
|
||||||
|
v-if="selectedApp"
|
||||||
id="modal-app-info"
|
id="modal-app-info"
|
||||||
|
:app-id="selectedApp"
|
||||||
|
:antifeatures="antifeatures"
|
||||||
|
@ok="onInstallClick(selectedApp)"
|
||||||
@hide="selectedApp = undefined"
|
@hide="selectedApp = undefined"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<template #bot>
|
<template #bot>
|
||||||
<!-- INSTALL CUSTOM APP -->
|
<!-- INSTALL CUSTOM APP -->
|
||||||
<card-form
|
<card-form
|
||||||
|
@ -154,6 +159,7 @@
|
||||||
import { validationMixin } from 'vuelidate'
|
import { validationMixin } from 'vuelidate'
|
||||||
|
|
||||||
import CardDeckFeed from '@/components/CardDeckFeed'
|
import CardDeckFeed from '@/components/CardDeckFeed'
|
||||||
|
import AppCatalogDetails from './AppCatalogDetails'
|
||||||
import { required, appRepoUrl } from '@/helpers/validators'
|
import { required, appRepoUrl } from '@/helpers/validators'
|
||||||
import { randint } from '@/helpers/commons'
|
import { randint } from '@/helpers/commons'
|
||||||
|
|
||||||
|
@ -161,18 +167,20 @@ export default {
|
||||||
name: 'AppCatalog',
|
name: 'AppCatalog',
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
CardDeckFeed
|
CardDeckFeed,
|
||||||
|
AppCatalogDetails
|
||||||
},
|
},
|
||||||
|
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
queries: [
|
queries: [
|
||||||
['GET', 'apps/catalog?full&with_categories']
|
['GET', 'apps/catalog?full&with_categories&with_antifeatures']
|
||||||
],
|
],
|
||||||
|
|
||||||
// Data
|
// Data
|
||||||
apps: undefined,
|
apps: undefined,
|
||||||
selectedApp: undefined,
|
selectedApp: undefined,
|
||||||
|
antifeatures: undefined,
|
||||||
|
|
||||||
// Filtering options
|
// Filtering options
|
||||||
qualityOptions: [
|
qualityOptions: [
|
||||||
|
@ -291,6 +299,7 @@ export default {
|
||||||
data.categories.forEach(({ title, id, icon, subtags, description }) => {
|
data.categories.forEach(({ title, id, icon, subtags, description }) => {
|
||||||
this.categories.push({ text: title, value: id, icon, subtags, description })
|
this.categories.push({ text: title, value: id, icon, subtags, description })
|
||||||
})
|
})
|
||||||
|
this.antifeatures = Object.fromEntries(data.antifeatures.map((af) => ([af.id, af])))
|
||||||
},
|
},
|
||||||
|
|
||||||
setCategory () {
|
setCategory () {
|
||||||
|
@ -301,7 +310,8 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
// INSTALL APP
|
// INSTALL APP
|
||||||
async onInstallClick (app) {
|
async onInstallClick (appId) {
|
||||||
|
const app = this.apps.find((app) => app.id === appId)
|
||||||
if (!app.decent_quality) {
|
if (!app.decent_quality) {
|
||||||
const confirmed = await this.$askConfirmation(this.$i18n.t('confirm_install_app_' + app.state))
|
const confirmed = await this.$askConfirmation(this.$i18n.t('confirm_install_app_' + app.state))
|
||||||
if (!confirmed) return
|
if (!confirmed) return
|
||||||
|
|
166
app/src/views/app/AppCatalogDetails.vue
Normal file
166
app/src/views/app/AppCatalogDetails.vue
Normal file
|
@ -0,0 +1,166 @@
|
||||||
|
<template>
|
||||||
|
<b-modal
|
||||||
|
v-bind="$attrs" v-on="$listeners"
|
||||||
|
body-class="p-0"
|
||||||
|
static lazy size="lg"
|
||||||
|
:title="$t('app.preview.title')" :ok-title="$t('install')"
|
||||||
|
>
|
||||||
|
<b-overlay :show="app === undefined">
|
||||||
|
<template v-if="app">
|
||||||
|
<section class="p-3">
|
||||||
|
<h3>{{ app.name }}</h3>
|
||||||
|
|
||||||
|
<p v-if="app.alternatives" class="mt-3">
|
||||||
|
<strong v-t="'app.potential_alternative_to'" />
|
||||||
|
{{ app.alternatives }}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<vue-showdown :markdown="app.description" flavor="github" />
|
||||||
|
|
||||||
|
<b-img
|
||||||
|
v-if="app.image"
|
||||||
|
:src="app.image"
|
||||||
|
aria-hidden="true" class="d-block mb-3" fluid
|
||||||
|
/>
|
||||||
|
|
||||||
|
<p>{{ $t('app.preview.version', { version: app.version }) }}</p>
|
||||||
|
|
||||||
|
<b-button
|
||||||
|
v-if="app.demo"
|
||||||
|
:href="app.demo" variant="primary" target="_blank"
|
||||||
|
>
|
||||||
|
<icon iname="external-link" />
|
||||||
|
{{ $t('app.preview.try_demo') }}
|
||||||
|
</b-button>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<card-collapse
|
||||||
|
id="app-warning" flush variant="warning"
|
||||||
|
:title="$t('app.preview.before_install')"
|
||||||
|
>
|
||||||
|
<b-card-body>
|
||||||
|
<strong v-t="'app.antifeatures'" class="d-block mb-1" />
|
||||||
|
<ul class="antifeatures">
|
||||||
|
<li v-for="antifeature in app.antifeatures" :key="antifeature.id">
|
||||||
|
<icon :iname="antifeature.icon" class="md mr-1" />
|
||||||
|
{{ antifeature.title }}
|
||||||
|
<explain-what
|
||||||
|
:id="antifeature.id"
|
||||||
|
:title="antifeature.title"
|
||||||
|
:content="antifeature.description"
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<vue-showdown :markdown="app.preInstall" flavor="github" />
|
||||||
|
</b-card-body>
|
||||||
|
</card-collapse>
|
||||||
|
|
||||||
|
<card-collapse id="app-integration" flush :title="$t('app.preview.integration.title')">
|
||||||
|
<b-list-group flush tag="section">
|
||||||
|
<yuno-list-group-item variant="info">
|
||||||
|
{{ $t('app.preview.integration.archs') }} {{ app.integration.archs }}
|
||||||
|
</yuno-list-group-item>
|
||||||
|
<yuno-list-group-item :variant="app.integration.ldap ? 'success' : 'warning'">
|
||||||
|
{{ $t(`app.preview.integration.ldap.${app.integration.ldap}`) }}
|
||||||
|
</yuno-list-group-item>
|
||||||
|
<yuno-list-group-item :variant="app.integration.sso ? 'success' : 'warning'">
|
||||||
|
{{ $t(`app.preview.integration.sso.${app.integration.sso}`) }}
|
||||||
|
</yuno-list-group-item>
|
||||||
|
<yuno-list-group-item variant="info">
|
||||||
|
{{ $t(`app.preview.integration.multi_instance.${app.integration.multi_instance}`) }}
|
||||||
|
</yuno-list-group-item>
|
||||||
|
<yuno-list-group-item variant="info">
|
||||||
|
{{ $t('app.preview.integration.resources', app.integration.resources) }}
|
||||||
|
</yuno-list-group-item>
|
||||||
|
</b-list-group>
|
||||||
|
</card-collapse>
|
||||||
|
|
||||||
|
<card-collapse id="app-links" flush :title="$t('app.preview.links.title')">
|
||||||
|
<b-list-group flush tag="section">
|
||||||
|
<yuno-list-group-item v-for="[key, link] in app.links" :key="key" no-status>
|
||||||
|
<b-link :href="link" target="_blank">
|
||||||
|
{{ $t('app.preview.links.' + key) }}
|
||||||
|
</b-link>
|
||||||
|
</yuno-list-group-item>
|
||||||
|
</b-list-group>
|
||||||
|
</card-collapse>
|
||||||
|
</template>
|
||||||
|
</b-overlay>
|
||||||
|
</b-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import api from '@/api'
|
||||||
|
import CardCollapse from '@/components/CardCollapse'
|
||||||
|
import { formatI18nField } from '@/helpers/yunohostArguments'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'AppCatalogDetails',
|
||||||
|
|
||||||
|
components: {
|
||||||
|
CardCollapse
|
||||||
|
},
|
||||||
|
|
||||||
|
props: {
|
||||||
|
appId: { type: String, required: true },
|
||||||
|
antifeatures: { type: Object, required: true }
|
||||||
|
},
|
||||||
|
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
app: undefined
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async created () {
|
||||||
|
let { id, name, version, potential_alternative_to: alternatives, ...app } = await api.get('apps/manifest?app=' + this.appId)
|
||||||
|
const archs = app.integration.architectures
|
||||||
|
const integration = {
|
||||||
|
archs: Array.isArray(archs) ? archs.join(this.$i18n.t('words.separator')) : archs,
|
||||||
|
ldap: app.integration.ldap === '?' ? null : app.integration.ldap,
|
||||||
|
sso: app.integration.sso === '?' ? null : app.integration.sso,
|
||||||
|
multi_instance: app.integration.multi_instance,
|
||||||
|
resources: {
|
||||||
|
ram: app.integration.ram.runtime,
|
||||||
|
disk: app.integration.disk
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const links = [
|
||||||
|
...['website', 'admindoc', 'code'].map((key) => ([key, app.upstream[key]])),
|
||||||
|
['package', app.remote.url],
|
||||||
|
['forum', `https://forum.yunohost.org/tag/${id}`]
|
||||||
|
].filter(([key, val]) => !!val)
|
||||||
|
|
||||||
|
this.app = {
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
alternatives: alternatives && alternatives.length ? alternatives.join(this.$i18n.t('words.separator')) : null,
|
||||||
|
description: formatI18nField(app.doc.DESCRIPTION),
|
||||||
|
image: app.image,
|
||||||
|
demo: app.upstream.demo,
|
||||||
|
version,
|
||||||
|
preInstall: formatI18nField(app.notifications.pre_install.main),
|
||||||
|
antifeatures: app.antifeatures?.map((af) => this.antifeatures[af]),
|
||||||
|
integration,
|
||||||
|
links
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
::v-deep .modal-body .b-overlay-wrap {
|
||||||
|
min-height: 50vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.antifeatures {
|
||||||
|
padding-left: 1rem;
|
||||||
|
|
||||||
|
li {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
Loading…
Add table
Reference in a new issue