From b1b9e65b7bcb29fc15167f524bb1fcfa149d7f2b Mon Sep 17 00:00:00 2001 From: Axolotle Date: Tue, 6 Oct 2020 23:53:56 +0200 Subject: [PATCH] add AppInstall view/route --- app/src/helpers/yunohostArguments.js | 64 +++++++++ app/src/i18n/locales/en.json | 6 +- app/src/router/routes.js | 26 ++++ app/src/scss/main.scss | 34 +++-- app/src/views/app/AppInstall.vue | 191 +++++++++++++++++++++++++++ 5 files changed, 304 insertions(+), 17 deletions(-) create mode 100644 app/src/helpers/yunohostArguments.js create mode 100644 app/src/views/app/AppInstall.vue diff --git a/app/src/helpers/yunohostArguments.js b/app/src/helpers/yunohostArguments.js new file mode 100644 index 00000000..7abfaef9 --- /dev/null +++ b/app/src/helpers/yunohostArguments.js @@ -0,0 +1,64 @@ +import i18n from '@/i18n' +import store from '@/store' + +function formatI18nField (field) { + if (typeof field === 'string') return field + const { locale, fallbackLocale } = store.state + return field[locale] || field[fallbackLocale] || field.en +} + +export function formatYunoHostArgument (_arg) { + const arg = { + component: undefined, + label: formatI18nField(_arg.ask), + props: { id: _arg.name } + } + + // Some apps has `string` as type but expect a select since it has `choices` + if (_arg.choices !== undefined) { + arg.component = 'SelectItem' + arg.props.choices = _arg.choices + // Input + } else if ([undefined, 'string', 'number', 'password', 'email'].includes(_arg.type)) { + arg.component = 'InputItem' + if (![undefined, 'string'].includes(_arg.type)) { + arg.props.type = _arg.type + if (_arg.type === 'password') { + arg.description = i18n.t('good_practices_about_admin_password') + } + } + // Checkbox + } else if (_arg.type === 'boolean') { + arg.component = 'CheckboxItem' + arg.props.value = _arg.default || false + // Special (store related) + } else if (['user', 'domain'].includes(_arg.type)) { + arg.component = 'SelectItem' + arg.link = { name: _arg.type + '-list', text: i18n.t(`manage_${_arg.type}s`) } + arg.props = { ...arg.props, ...store.getters[_arg.type + 'sAsOptions'] } + // Unknown from the specs, try to display it as an input[text] + // FIXME throw an error instead ? + } else { + arg.component = 'InputItem' + } + + // Required + arg.props.required = _arg.optional !== true + // Default value + if (_arg.default) { + arg.props.value = _arg.default + } + // Help message + if (_arg.help) { + arg.description = formatI18nField(_arg.help) + } + // Example + if (_arg.example) { + arg.example = _arg.example + if (arg.component === 'InputItem') { + arg.props.placeholder = arg.example + } + } + + return arg +} diff --git a/app/src/i18n/locales/en.json b/app/src/i18n/locales/en.json index b5ce994b..fbaf0791 100644 --- a/app/src/i18n/locales/en.json +++ b/app/src/i18n/locales/en.json @@ -64,7 +64,7 @@ "confirm_firewall_open": "Are you sure you want to open port {port} (protocol: {protocol}, connection: {connection})", "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_domain_root": "Are you sure you want to install this application on '/'? You will not be able to install any other app on {domain}", "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?", @@ -140,7 +140,7 @@ "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", + "form_input_example": "Example: {example}", "from_to": "from {0} to {1}", "good_practices_about_admin_password": "You are now about to define a new admin password. The password should be at least 8 characters - though it is good practice to use longer password (i.e. a passphrase) and/or to use various kind of characters (uppercase, lowercase, digits and special characters).", "good_practices_about_user_password": "You are now about to define a new user password. The password should be at least 8 characters - though it is good practice to use longer password (i.e. a passphrase) and/or to use various kind of characters (uppercase, lowercase, digits and special characters).", @@ -191,7 +191,7 @@ "ipv6": "IPv6", "issues": "{count} issues", "label": "Label", - "label_for_manifestname": "Label for %s", + "label_for_manifestname": "Label for {name}", "last_ran": "Last time ran:", "license": "License", "loading": "Loading …", diff --git a/app/src/router/routes.js b/app/src/router/routes.js index 2b643a45..efe45318 100644 --- a/app/src/router/routes.js +++ b/app/src/router/routes.js @@ -188,6 +188,32 @@ const routes = [ ] } }, + { + name: 'app-install', + path: '/apps/install/:id', + component: () => import(/* webpackChunkName: "views/apps" */ '@/views/app/AppInstall'), + props: true, + meta: { + breadcrumb: [ + { name: 'app-list', trad: 'applications' }, + { name: 'app-catalog', trad: 'catalog' }, + { name: 'app-catalog', trad: 'install_name', param: 'id' } + ] + } + }, + { + name: 'app-install-custom', + path: '/apps/install-custom/:id', + component: () => import(/* webpackChunkName: "views/apps" */ '@/views/app/AppInstall'), + props: true, + meta: { + breadcrumb: [ + { name: 'app-list', trad: 'applications' }, + { name: 'app-catalog', trad: 'catalog' }, + { name: 'app-catalog', trad: 'install_name', param: 'id' } + ] + } + }, /* ────────────────╮ │ SYSTEM UPDATE │ diff --git a/app/src/scss/main.scss b/app/src/scss/main.scss index 6d144a97..647d8d42 100644 --- a/app/src/scss/main.scss +++ b/app/src/scss/main.scss @@ -49,21 +49,27 @@ body { display: block; } -.col-form-label { - @include media-breakpoint-up(xs) { - width: 100%; +// elems with inside and eventually .sep inside +.row-line { + &:hover > div :first-child { + background-color: rgba(0, 0, 0, 0.05); + border-radius: 0.2rem; + padding: 0 .5rem; + margin-right: -.5rem; + margin-left: -.5rem; } - @include media-breakpoint-up(sm) { - flex-basis: 40%; - } - @include media-breakpoint-up(md) { - flex-basis: 30%; - } - @include media-breakpoint-up(lg) { - flex-basis: 25%; - } - @include media-breakpoint-up(xl) { - flex-basis: 20%; + + & > div { + align-self: flex-start; + display: flex; + align-items: center; + // display a dashed line between col items + .sep { + height: 0; + border-top: 1px dashed rgba(0, 0, 0, 0.25); + flex-grow: 1; + margin: 0 .75rem; + } } } diff --git a/app/src/views/app/AppInstall.vue b/app/src/views/app/AppInstall.vue new file mode 100644 index 00000000..6db49aee --- /dev/null +++ b/app/src/views/app/AppInstall.vue @@ -0,0 +1,191 @@ + + + + +