add custom app install card and update with install routes

This commit is contained in:
Axolotle 2020-10-03 16:00:00 +02:00
parent ad25c7373e
commit 3fde9d84bb
5 changed files with 157 additions and 36 deletions

View file

@ -20,6 +20,7 @@
"app_install_custom_no_manifest": "No manifest.json file",
"app_make_default": "Make default",
"app_no_actions": "This application doesn't have any actions",
"app_not_found": "Unable to find an app matching your criteria.",
"app_show_categories": "Show categories",
"app_state_inprogress": "not yet working",
"app_state_inprogress_explanation": "This maintainer of this app declared that this application is not ready yet for production use. BE CAREFUL!",
@ -64,8 +65,9 @@
"confirm_firewall_close": "Are you sure you want to close port {port} (protocol: {protocol}, connection: {connection})",
"confirm_install_custom_app": "WARNING! Installing 3rd party applications may compromise the integrity and security of your system. You should probably NOT install it unless you know what you are doing. Are you willing to take that risk?",
"confirm_install_domain_root": "You will not be able to install any other app on %s. Continue?",
"confirm_install_app_warning": "Warning: this application may work but is not well-integrated in YunoHost. Some features such as single sign-on and backup/restore might not be available.",
"confirm_install_app_danger": "WARNING! This application is still experimental (if not explicitly not working) and it is likely to break your system! You should probably NOT install it unless you know what you are doing. Are you willing to take that risk?",
"confirm_app_install": "Are you sure you want to install this application?",
"confirm_install_app_lowquality": "Warning: this application may work but is not well-integrated in YunoHost. Some features such as single sign-on and backup/restore might not be available.",
"confirm_install_app_inprogress": "WARNING! This application is still experimental (if not explicitly not working) and it is likely to break your system! You should probably NOT install it unless you know what you are doing. Are you willing to take that risk?",
"confirm_migrations_skip": "Skipping migrations is not recommended. Are you sure you want to do that?",
"confirm_postinstall": "You are about to launch the post-installation process on the domain {domain}. It may take a few minutes, *do not interrupt the operation*.",
"confirm_restore": "Are you sure you want to restore {name}?",
@ -135,7 +137,8 @@
"username_syntax": "Invalid username: Must be lower-case alphanumeric and underscore characters only",
"domain_syntax": "Invalid domain name: Must be lower-case alphanumeric, dot and dash characters only",
"dynDomain_syntax": "Invalid domain name: Must be lower-case alphanumeric and dash characters only",
"firewall_port_already": "Port {port} is already {state} (protocol: {protocol}; connection: {connection})"
"firewall_port_already": "Port {port} is already {state} (protocol: {protocol}; connection: {connection})",
"not_github_link": "Url must be a valid Github link to a repository"
},
"form_input_example": "Example: %s",
"from_to": "from {0} to {1}",
@ -179,7 +182,7 @@
"inactive": "Inactive",
"infos": "Info",
"install": "Install",
"install_name": "Install %s",
"install_name": "Install {id}",
"install_time": "Install time",
"installation_complete": "Installation complete",
"installed": "Installed",

View file

@ -178,13 +178,13 @@ const routes = [
}
},
{
name: 'app-catalog-home',
name: 'app-catalog',
path: '/apps/catalog',
component: () => import(/* webpackChunkName: "views/apps-catalog" */ '@/views/app/AppCatalog'),
meta: {
breadcrumb: [
{ name: 'app-list', trad: 'applications' },
{ name: 'app-catalog-home', trad: 'catalog' }
{ name: 'app-catalog', trad: 'catalog' }
]
}
},

View file

@ -18,6 +18,11 @@
$alert-padding-x: 1rem;
$color-purple: #9932cc;
$theme-colors: (
"best": $color-purple
);
/*

View file

@ -20,10 +20,10 @@
</b-input-group-prepend>
<b-form-input
id="search-input" :placeholder="$t('search_for_apps')"
v-model="search" @input="onSearchInput"
v-model="search" @input="setCategory"
/>
<b-input-group-append>
<b-select v-model="quality" :options="qualityOptions" />
<b-select v-model="quality" :options="qualityOptions" @change="setCategory" />
</b-input-group-append>
</b-input-group>
@ -43,7 +43,7 @@
</b-card-group>
<!-- APPS CARDS -->
<b-card-group v-else deck>
<b-card-group v-else-if="filteredApps.length > 0" deck>
<b-card no-body v-for="app in filteredApps" :key="app.id">
<b-card-body class="d-flex flex-column">
<b-card-title class="d-flex">
@ -77,7 +77,7 @@
<icon iname="book" /> {{ $t('readme') }}
</b-button>
<b-button v-if="app.isInstallable" :variant="app.color">
<b-button v-if="app.isInstallable" :variant="app.color" @click="onAppInstallClick(app)">
<icon iname="plus" /> {{ $t('install') }} <icon v-if="app.color === 'danger'" class="ml-1" iname="warning" />
</b-button>
<b-button v-else :variant="app.color" disabled>
@ -86,20 +86,81 @@
</b-button-group>
</b-card>
</b-card-group>
<!-- NO APPS -->
<b-alert
v-else
variant="warning" show class="mt-4"
>
<icon iname="exclamation-triangle" /> {{ $t('app_not_found') }}
</b-alert>
<!-- INSTALL CUSTOM APP -->
<b-card class="basic-form mt-5">
<template v-slot:header>
<h2><icon iname="download" /> {{ $t('custom_app_install') }}</h2>
</template>
<b-form id="custom-app-form" @submit.prevent="onSubmit">
<b-alert variant="warning" show>
<icon iname="exclamation-triangle" /> {{ $t('confirm_install_custom_app') }}
</b-alert>
<!-- URL -->
<input-helper
id="url" :label="$t('url')"
v-model="form.url" placeholder="https://github.com/USER/REPOSITORY"
:state="form.isValid" :error="form.error" @input="validateUrl"
>
<template v-slot:description>
<icon iname="github" /> {{ $t('custom_app_url_only_github') }}
</template>
</input-helper>
</b-form>
<template v-slot:footer>
<b-button
variant="success"
:disabled="form.url === '' || form.isValid === false"
v-b-modal.custom-app-install-modal
>
{{ $t('install') }}
</b-button>
</template>
</b-card>
<!-- CONFIRM APP INSTALL MODAL -->
<b-modal
id="app-install-modal" centered ref="app-install-modal"
:ok-title="$t('install')" :title="$t('confirm_app_install')"
:header-bg-variant="selectedApp.color"
:header-text-variant="selectedApp.color === 'danger' ? 'light' : 'dark'"
@ok="goToAppInstallForm"
>
{{ $t('confirm_install_app_' + selectedApp.state) }}
</b-modal>
<!-- CONFIRM CUSTOM APP INSTALL MODAL -->
<b-modal
id="custom-app-install-modal" centered
:ok-title="$t('install')" :title="$t('confirm_app_install')"
header-bg-variant="danger" header-text-variant="light"
@ok="goToCustomAppInstallForm"
>
{{ $t('confirm_install_custom_app') }}
</b-modal>
</div>
</template>
<script>
import api from '@/helpers/api'
import InputHelper from '@/components/InputHelper'
export default {
name: 'AppCatalog',
data () {
return {
category: null,
search: '',
quality: 'all',
searchAppsKeys: ['id', 'state', 'manifest.name'],
qualityOptions: [
{ value: 'isHighQuality', text: this.$i18n.t('only_highquality_apps') },
@ -107,12 +168,26 @@ export default {
{ value: 'isWorking', text: this.$i18n.t('only_working_apps') },
{ value: 'all', text: this.$i18n.t('all_apps') }
],
// computed/filled from api data
// Computed/filled from api data
categories: [
{ text: this.$i18n.t('app_choose_category'), value: null },
{ text: this.$i18n.t('all_apps'), value: 'all', icon: 'search' }
],
apps: undefined
apps: undefined,
// Set by user inputs
category: null,
search: '',
quality: 'all',
selectedApp: {
// Set some basic values to avoid modal errors
state: 'lowquality',
color: 'warning'
},
form: {
url: '',
isValid: null,
error: this.$i18n.t('form_errors.not_github_link')
}
}
},
@ -199,16 +274,47 @@ export default {
return 'danger'
},
onSearchInput () {
setCategory () {
// allow search without selecting a category
if (this.category === null) {
this.category = 'all'
}
},
// INSTALL APP METHODS
onAppInstallClick (app) {
this.selectedApp = app
if (!app.isDecentQuality) {
// Ask for confirmation
this.$refs['app-install-modal'].show()
} else {
this.goToAppInstallForm()
}
},
goToAppInstallForm () {
this.$router.push({ name: 'app-install', params: { id: this.selectedApp.id } })
},
// INSTALL CUSTOM APP METHODS
validateUrl () {
const match = this.form.url.match(/^https:\/\/github.com\/[a-zA-Z0-9-_.]+\/[a-zA-Z0-9-_.]+[/]?$/)
this.form.isValid = match ? null : false
},
goToCustomAppInstallForm () {
this.$router.push({ name: 'app-install-custom', params: { id: this.form.url } })
}
},
created () {
this.fetchData()
},
components: {
InputHelper
}
}
</script>
@ -222,28 +328,35 @@ select {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
.card {
margin-top: 2rem;
flex-basis: 100%;
min-height: 12rem;
@include media-breakpoint-up(md) {
flex-basis: 50%;
max-width: calc(50% - 30px);
}
@include media-breakpoint-up(lg) {
flex-basis: 33%;
max-width: calc(33.3% - 30px);
.card-deck {
.card {
margin-top: 2rem;
flex-basis: 100%;
@include media-breakpoint-up(md) {
flex-basis: 50%;
max-width: calc(50% - 30px);
}
@include media-breakpoint-up(lg) {
flex-basis: 33%;
max-width: calc(33.3% - 30px);
}
}
}
.category-card {
min-height: 10rem;
border: 0;
.app-card {
min-height: 12rem;
}
.btn {
width: 100%;
height: 100%;
.category-card {
@include media-breakpoint-up(sm) {
min-height: 10rem;
}
border: 0;
.btn {
padding: 1rem;
width: 100%;
height: 100%;
}
}
}

View file

@ -12,7 +12,7 @@
/>
</b-input-group>
<div class="buttons">
<b-button variant="success" :to="{ name: 'app-catalog-home' }">
<b-button variant="success" :to="{ name: 'app-catalog' }">
<icon iname="plus" /> {{ $t('install') }}
</b-button>
</div>