From 4cf322f6ad29c9a3dea88110ebe0b33da5b1c252 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Wed, 30 Sep 2020 17:53:31 -0400 Subject: [PATCH 01/14] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20implement=20initia?= =?UTF-8?q?l=20confirm=20service=20and=20decorator?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit for super easy application of confirmation dialogs on existing action --- ui/core/app/routes/scopes/scope/projects.js | 5 +- ui/core/app/services/confirm.js | 53 +++++++++++++++++++++ ui/core/app/utilities/confirm.js | 23 +++++++++ ui/core/config/environment.js | 2 + ui/core/tests/unit/services/confirm-test.js | 12 +++++ ui/core/translations/en-us.yaml | 5 +- 6 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 ui/core/app/services/confirm.js create mode 100644 ui/core/app/utilities/confirm.js create mode 100644 ui/core/tests/unit/services/confirm-test.js diff --git a/ui/core/app/routes/scopes/scope/projects.js b/ui/core/app/routes/scopes/scope/projects.js index efd9d71f8..77f57f12d 100644 --- a/ui/core/app/routes/scopes/scope/projects.js +++ b/ui/core/app/routes/scopes/scope/projects.js @@ -2,6 +2,7 @@ import Route from '@ember/routing/route'; import { inject as service } from '@ember/service'; import { action } from '@ember/object'; import loading from 'ember-loading/decorator'; +import { confirm } from '../../../utilities/confirm'; export default class ScopesScopeProjectsRoute extends Route { // =services @@ -9,6 +10,7 @@ export default class ScopesScopeProjectsRoute extends Route { @service intl; @service notify; @service session; + @service confirm; // =methods @@ -84,12 +86,13 @@ export default class ScopesScopeProjectsRoute extends Route { */ @action @loading + @confirm('questions.delete-confirm') async delete(project) { try { await project.destroyRecord(); + await this.replaceWith('scopes.scope.projects'); this.refresh(); this.notify.success(this.intl.t('notifications.delete-success')); - this.transitionTo('scopes.scope.projects'); } catch (error) { // TODO: replace with translated strings this.notify.error(error.message, { closeAfter: null }); diff --git a/ui/core/app/services/confirm.js b/ui/core/app/services/confirm.js new file mode 100644 index 000000000..d3c0e4fe8 --- /dev/null +++ b/ui/core/app/services/confirm.js @@ -0,0 +1,53 @@ +import Service from '@ember/service'; +import { getOwner } from '@ember/application'; +import { bind } from '@ember/runloop'; +import { Promise, resolve } from 'rsvp'; +import config from '../config/environment'; + +export default class ConfirmService extends Service { + + // =attributes + + /** + * When the confirm service is disabled, it always returns resolving promises. + * @type {boolean} + */ + enabled = config.enableConfirmService; + + /** + * The window's confirm function, bound to the window scope. + * @type {Function} + */ + get confirmFn() { + // The Ember way of accessing globals... + const document = + getOwner(this).lookup('service:-document').documentElement; + // defaultView === window, but without using globals directly + const windowRef = document.parentNode.defaultView; + const { confirm } = windowRef; + return bind(windowRef, confirm); + } + + // =methods + + /** + * When the service is enabled, returns a promise that resolves if the user + * accepts the confirmation, otherwise rejects. If the service is disabled, + * this method always returns a resolving promise. + * @param {string} text + * @return {Promise} + */ + confirm(text) { + if (this.enabled) { + return new Promise((resolve, reject) => { + if (this.confirmFn(text)) { + return resolve(); + } else { + return reject(); + } + }); + } else { + return resolve(); + } + } +} diff --git a/ui/core/app/utilities/confirm.js b/ui/core/app/utilities/confirm.js new file mode 100644 index 000000000..755ff2f59 --- /dev/null +++ b/ui/core/app/utilities/confirm.js @@ -0,0 +1,23 @@ +import { getOwner } from '@ember/application'; + +/** + * Decorates a method whereby calls to the method will first request + * confirmation via the confirmation service. If the user accepts, the + * original method is executed. If the user denies, it is not. + */ +export function confirm(confirmationTextKey) { + return function (_target, _propertyKey, desc) { + const method = desc.value; + desc.value = function () { + const owner = getOwner(this); + const confirmService = owner.lookup('service:confirm'); + const intl = owner.lookup('service:intl'); + const text = intl.t(confirmationTextKey); + return confirmService.confirm(text) + // If user confirmed, execute the decorated method + .then(() => method.apply(this, arguments)) + // If the user denied, gracefully handle promise rejection with no-op + .catch(() => {}); + }; + } +} diff --git a/ui/core/config/environment.js b/ui/core/config/environment.js index f2c6362f9..a6f794d02 100644 --- a/ui/core/config/environment.js +++ b/ui/core/config/environment.js @@ -34,6 +34,7 @@ module.exports = function (environment) { appName: APP_NAME, notifyTimeout: 4000, + enableConfirmService: true, contentSecurityPolicyHeader: 'Content-Security-Policy', contentSecurityPolicyMeta: true, @@ -98,6 +99,7 @@ module.exports = function (environment) { // Notification timeout should be 0 for fast tests ENV.notifyTimeout = 0; + ENV.enableConfirmService = false; } if (environment === 'production') { diff --git a/ui/core/tests/unit/services/confirm-test.js b/ui/core/tests/unit/services/confirm-test.js new file mode 100644 index 000000000..c573fc2ed --- /dev/null +++ b/ui/core/tests/unit/services/confirm-test.js @@ -0,0 +1,12 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Service | confirm', function(hooks) { + setupTest(hooks); + + // Replace this with your real tests. + test('it exists', function(assert) { + let service = this.owner.lookup('service:confirm'); + assert.ok(service); + }); +}); diff --git a/ui/core/translations/en-us.yaml b/ui/core/translations/en-us.yaml index a86a36263..413f0bae4 100644 --- a/ui/core/translations/en-us.yaml +++ b/ui/core/translations/en-us.yaml @@ -17,4 +17,7 @@ titles: global: Global actions: Actions descriptions: - empty-set: There are no items to display yet. You may be able to add items or try back later. \ No newline at end of file + empty-set: There are no items to display yet. You may be able to add items or try back later. +questions: + delete-confirm: Are you sure you want to delete this resource? + remove-confirm: Are you sure you want to remove this association? From 51e6fe2db049d4a2197b52e4d3c85eacceb0ec88 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Wed, 30 Sep 2020 18:10:22 -0400 Subject: [PATCH 02/14] =?UTF-8?q?test:=20=F0=9F=92=8D=20add=20confirm=20se?= =?UTF-8?q?rvice=20and=20decorator=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui/core/package.json | 3 +- ui/core/tests/acceptance/projects-test.js | 23 +++++++ yarn.lock | 79 ++++++++++++++++++++++- 3 files changed, 103 insertions(+), 2 deletions(-) diff --git a/ui/core/package.json b/ui/core/package.json index 6b6547fbd..a22b04aee 100644 --- a/ui/core/package.json +++ b/ui/core/package.json @@ -86,7 +86,8 @@ "qunit-dom": "^1.0.0", "rose": "*", "sass": "^1.26.5", - "sass-lint": "^1.13.1" + "sass-lint": "^1.13.1", + "sinon": "^9.1.0" }, "engines": { "node": "10.* || >= 12" diff --git a/ui/core/tests/acceptance/projects-test.js b/ui/core/tests/acceptance/projects-test.js index 0483a56c8..9a3830c40 100644 --- a/ui/core/tests/acceptance/projects-test.js +++ b/ui/core/tests/acceptance/projects-test.js @@ -10,6 +10,7 @@ import { //currentSession, //invalidateSession, } from 'ember-simple-auth/test-support'; +import sinon from 'sinon'; module('Acceptance | projects', function (hooks) { setupApplicationTest(hooks); @@ -141,6 +142,28 @@ module('Acceptance | projects', function (hooks) { assert.equal(getProjectScopesCount(), initialProjectScopesCount - 1); }); + test('can delete project with confirmation acceptance', async function (assert) { + assert.expect(2); + const confirmService = this.owner.lookup('service:confirm'); + confirmService.enabled = true; + window.confirm = sinon.fake.returns(true); + await visit(existingProjectURL); + await click('.rose-layout-page-actions .rose-dropdown-button-danger'); + assert.equal(getProjectScopesCount(), initialProjectScopesCount - 1); + assert.ok(window.confirm.calledOnce); + }); + + test('cannot delete project with confirmation denial', async function (assert) { + assert.expect(2); + const confirmService = this.owner.lookup('service:confirm'); + confirmService.enabled = true; + window.confirm = sinon.fake.returns(false); + await visit(existingProjectURL); + await click('.rose-layout-page-actions .rose-dropdown-button-danger'); + assert.equal(getProjectScopesCount(), initialProjectScopesCount); + assert.ok(window.confirm.calledOnce); + }); + test('saving an existing project with invalid fields displays error messages', async function (assert) { assert.expect(2); this.server.patch('/scopes/:id', () => { diff --git a/yarn.lock b/yarn.lock index f13b0ae16..32b4f960c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2975,6 +2975,42 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd" integrity sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow== +"@sinonjs/commons@^1", "@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0", "@sinonjs/commons@^1.7.2": + version "1.8.1" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.1.tgz#e7df00f98a203324f6dc7cc606cad9d4a8ab2217" + integrity sha512-892K+kWUUi3cl+LlqEWIDrhvLgdL79tECi8JZUyq6IviKy/DNhuzCRlbHUjxK89f4ypPMMaFnFuR9Ie6DoIMsw== + dependencies: + type-detect "4.0.8" + +"@sinonjs/fake-timers@^6.0.0", "@sinonjs/fake-timers@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz#293674fccb3262ac782c7aadfdeca86b10c75c40" + integrity sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA== + dependencies: + "@sinonjs/commons" "^1.7.0" + +"@sinonjs/formatio@^5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@sinonjs/formatio/-/formatio-5.0.1.tgz#f13e713cb3313b1ab965901b01b0828ea6b77089" + integrity sha512-KaiQ5pBf1MpS09MuA0kp6KBQt2JUOQycqVG1NZXvzeaXe5LGFqAKueIS0bw4w0P9r7KuBSVdUk5QjXsUdu2CxQ== + dependencies: + "@sinonjs/commons" "^1" + "@sinonjs/samsam" "^5.0.2" + +"@sinonjs/samsam@^5.0.2", "@sinonjs/samsam@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-5.1.0.tgz#3afe719232b541bb6cf3411a4c399a188de21ec0" + integrity sha512-42nyaQOVunX5Pm6GRJobmzbS7iLI+fhERITnETXzzwDZh+TtDr/Au3yAvXVjFmZ4wEUaE4Y3NFZfKv0bV0cbtg== + dependencies: + "@sinonjs/commons" "^1.6.0" + lodash.get "^4.4.2" + type-detect "^4.0.8" + +"@sinonjs/text-encoding@^0.7.1": + version "0.7.1" + resolved "https://registry.yarnpkg.com/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz#8da5c6530915653f3a1f38fd5f101d8c3f8079c5" + integrity sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ== + "@storybook/addon-actions@^6.0.21": version "6.0.21" resolved "https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-6.0.21.tgz#0de1d109d4b1eb99f644bbe84e74c25cfd2b1b6b" @@ -8286,7 +8322,7 @@ dezalgo@^1.0.0: asap "^2.0.0" wrappy "1" -diff@^4.0.1: +diff@^4.0.1, diff@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== @@ -13501,6 +13537,11 @@ junk@^3.1.0: resolved "https://registry.yarnpkg.com/junk/-/junk-3.1.0.tgz#31499098d902b7e98c5d9b9c80f43457a88abfa1" integrity sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ== +just-extend@^4.0.2: + version "4.1.1" + resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-4.1.1.tgz#158f1fdb01f128c411dc8b286a7b4837b3545282" + integrity sha512-aWgeGFW67BP3e5181Ep1Fv2v8z//iBJfrvyTnq8wG86vEESwmonn1zPBJ0VfmT9CJq2FIT0VsETtrNFm2a+SHA== + keyv@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.0.0.tgz#44923ba39e68b12a7cec7df6c3268c031f2ef373" @@ -14833,6 +14874,17 @@ nice-try@^1.0.4: resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== +nise@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/nise/-/nise-4.0.4.tgz#d73dea3e5731e6561992b8f570be9e363c4512dd" + integrity sha512-bTTRUNlemx6deJa+ZyoCUTRvH3liK5+N6VQZ4NIw90AgDXY6iPnsqplNFf6STcj+ePk0H/xqxnP75Lr0J0Fq3A== + dependencies: + "@sinonjs/commons" "^1.7.0" + "@sinonjs/fake-timers" "^6.0.0" + "@sinonjs/text-encoding" "^0.7.1" + just-extend "^4.0.2" + path-to-regexp "^1.7.0" + no-case@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.3.tgz#c21b434c1ffe48b39087e86cfb4d2582e9df18f8" @@ -15703,6 +15755,13 @@ path-to-regexp@0.1.7: resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= +path-to-regexp@^1.7.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a" + integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA== + dependencies: + isarray "0.0.1" + path-type@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" @@ -17745,6 +17804,19 @@ single-line-log@^1.1.2: dependencies: string-width "^1.0.1" +sinon@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/sinon/-/sinon-9.1.0.tgz#4afc90707c8e360fe051398eed2d3b197980ffc3" + integrity sha512-9zQShgaeylYH6qtsnNXlTvv0FGTTckuDfHBi+qhgj5PvW2r2WslHZpgc3uy3e/ZAoPkqaOASPi+juU6EdYRYxA== + dependencies: + "@sinonjs/commons" "^1.7.2" + "@sinonjs/fake-timers" "^6.0.1" + "@sinonjs/formatio" "^5.0.1" + "@sinonjs/samsam" "^5.1.0" + diff "^4.0.2" + nise "^4.0.4" + supports-color "^7.1.0" + slash@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" @@ -19076,6 +19148,11 @@ type-check@~0.3.2: dependencies: prelude-ls "~1.1.2" +type-detect@4.0.8, type-detect@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== + type-fest@^0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1" From fa34a292ca8b9e518ef3b183029f39d0e20880b4 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Wed, 30 Sep 2020 18:41:55 -0400 Subject: [PATCH 03/14] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20enable=20delete=20?= =?UTF-8?q?confirm=20for=20all=20delete=20actions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui/core/app/routes/scopes/scope/auth-methods.js | 4 +++- .../scopes/scope/auth-methods/auth-method/accounts.js | 6 ++++-- ui/core/app/routes/scopes/scope/groups.js | 4 +++- .../scopes/scope/projects/project/host-catalogs.js | 10 ++++++---- .../project/host-catalogs/host-catalog/host-sets.js | 8 +++++--- .../project/host-catalogs/host-catalog/hosts.js | 10 ++++++---- .../routes/scopes/scope/projects/project/targets.js | 4 +++- ui/core/app/routes/scopes/scope/roles.js | 4 +++- ui/core/app/routes/scopes/scope/users.js | 4 +++- .../auth-method/accounts/account/-actions.hbs | 2 +- .../project/host-catalogs/host-catalog/-actions.hbs | 2 +- .../host-catalogs/host-catalog/host-sets/-actions.hbs | 4 ++-- .../host-catalogs/host-catalog/hosts/-actions.hbs | 4 ++-- 13 files changed, 42 insertions(+), 24 deletions(-) diff --git a/ui/core/app/routes/scopes/scope/auth-methods.js b/ui/core/app/routes/scopes/scope/auth-methods.js index f7bbe65c5..0b4072276 100644 --- a/ui/core/app/routes/scopes/scope/auth-methods.js +++ b/ui/core/app/routes/scopes/scope/auth-methods.js @@ -2,6 +2,7 @@ import Route from '@ember/routing/route'; import { inject as service } from '@ember/service'; import { action } from '@ember/object'; import loading from 'ember-loading/decorator'; +import { confirm } from '../../../utilities/confirm'; export default class ScopesScopeAuthMethodsRoute extends Route { // =services @@ -71,10 +72,11 @@ export default class ScopesScopeAuthMethodsRoute extends Route { */ @action @loading + @confirm('questions.delete-confirm') async delete(authMethod) { try { await authMethod.destroyRecord(); - await this.transitionTo('scopes.scope.auth-methods'); + await this.replaceWith('scopes.scope.auth-methods'); this.refresh(); this.notify.success(this.intl.t('notifications.delete-success')); } catch (error) { diff --git a/ui/core/app/routes/scopes/scope/auth-methods/auth-method/accounts.js b/ui/core/app/routes/scopes/scope/auth-methods/auth-method/accounts.js index fbd28041a..306acb0b8 100644 --- a/ui/core/app/routes/scopes/scope/auth-methods/auth-method/accounts.js +++ b/ui/core/app/routes/scopes/scope/auth-methods/auth-method/accounts.js @@ -2,6 +2,7 @@ import Route from '@ember/routing/route'; import { action } from '@ember/object'; import { inject as service } from '@ember/service'; import loading from 'ember-loading/decorator'; +import { confirm } from '../../../../../utilities/confirm'; export default class ScopesScopeAuthMethodsAuthMethodAccountsRoute extends Route { // =services @@ -71,10 +72,11 @@ export default class ScopesScopeAuthMethodsAuthMethodAccountsRoute extends Route */ @action @loading - async delete(account) { + @confirm('questions.delete-confirm') + async deleteAccount(account) { try { await account.destroyRecord(); - await this.transitionTo('scopes.scope.auth-methods.auth-method.accounts'); + await this.replaceWith('scopes.scope.auth-methods.auth-method.accounts'); this.refresh(); this.notify.success(this.intl.t('notifications.delete-success')); } catch (error) { diff --git a/ui/core/app/routes/scopes/scope/groups.js b/ui/core/app/routes/scopes/scope/groups.js index 66ac9d8bd..26b5a3f61 100644 --- a/ui/core/app/routes/scopes/scope/groups.js +++ b/ui/core/app/routes/scopes/scope/groups.js @@ -2,6 +2,7 @@ import Route from '@ember/routing/route'; import { inject as service } from '@ember/service'; import { action } from '@ember/object'; import loading from 'ember-loading/decorator'; +import { confirm } from '../../../utilities/confirm'; export default class ScopesScopeGroupsRoute extends Route { // =services @@ -68,12 +69,13 @@ export default class ScopesScopeGroupsRoute extends Route { */ @action @loading + @confirm('questions.delete-confirm') async delete(group) { try { await group.destroyRecord(); + await this.replaceWith('scopes.scope.groups'); this.refresh(); this.notify.success(this.intl.t('notifications.delete-success')); - await this.transitionTo('scopes.scope.groups'); } catch (error) { //TODO: replace with translated strings this.notify.error(error.message, { closeAfter: null }); diff --git a/ui/core/app/routes/scopes/scope/projects/project/host-catalogs.js b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs.js index 3cc9e212d..28058850f 100644 --- a/ui/core/app/routes/scopes/scope/projects/project/host-catalogs.js +++ b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs.js @@ -2,6 +2,7 @@ import Route from '@ember/routing/route'; import { inject as service } from '@ember/service'; import { action } from '@ember/object'; import loading from 'ember-loading/decorator'; +import { confirm } from '../../../../../utilities/confirm'; export default class ScopesScopeProjectsProjectHostCatalogsRoute extends Route { // =services @@ -61,16 +62,17 @@ export default class ScopesScopeProjectsProjectHostCatalogsRoute extends Route { /** * Deletes the host catalog and redirects to index. - * @param {ProjectModel} project + * @param {HostCatalogModel} hostCatalog */ @action @loading - async delete(project) { + @confirm('questions.delete-confirm') + async delete(hostCatalog) { try { - await project.destroyRecord(); + await hostCatalog.destroyRecord(); + await this.replaceWith('scopes.scope.projects.project.host-catalogs'); this.refresh(); this.notify.success(this.intl.t('notifications.delete-success')); - this.transitionTo('scopes.scope.projects.project.host-catalogs'); } catch (error) { // TODO: replace with translated strings this.notify.error(error.message, { closeAfter: null }); diff --git a/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/host-sets.js b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/host-sets.js index affdabbe0..3591b4a4b 100644 --- a/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/host-sets.js +++ b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/host-sets.js @@ -2,6 +2,7 @@ import Route from '@ember/routing/route'; import { inject as service } from '@ember/service'; import { action } from '@ember/object'; import loading from 'ember-loading/decorator'; +import { confirm } from '../../../../../../../utilities/confirm'; export default class ScopesScopeProjectsProjectHostCatalogsHostCatalogHostSetsRoute extends Route { // =services @@ -70,13 +71,14 @@ export default class ScopesScopeProjectsProjectHostCatalogsHostCatalogHostSetsRo */ @action @loading - async delete(hostSet) { + @confirm('questions.delete-confirm') + async deleteHostSet(hostSet) { try { await hostSet.destroyRecord(); - this.notify.success(this.intl.t('notifications.delete-success')); - this.transitionTo( + await this.replaceWith( 'scopes.scope.projects.project.host-catalogs.host-catalog.host-sets' ); + this.notify.success(this.intl.t('notifications.delete-success')); } catch (error) { // TODO: replace with translated strings this.notify.error(error.message, { closeAfter: null }); diff --git a/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/hosts.js b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/hosts.js index 031975c46..42dd70019 100644 --- a/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/hosts.js +++ b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/hosts.js @@ -2,6 +2,7 @@ import Route from '@ember/routing/route'; import { inject as service } from '@ember/service'; import { action } from '@ember/object'; import loading from 'ember-loading/decorator'; +import { confirm } from '../../../../../../../utilities/confirm'; export default class ScopesScopeProjectsProjectHostCatalogsHostCatalogHostsRoute extends Route { // =services @@ -70,14 +71,15 @@ export default class ScopesScopeProjectsProjectHostCatalogsHostCatalogHostsRoute */ @action @loading - async delete(host) { + @confirm('questions.delete-confirm') + async deleteHost(host) { try { await host.destroyRecord(); - this.refresh(); - this.notify.success(this.intl.t('notifications.delete-success')); - this.transitionTo( + await this.replaceWith( 'scopes.scope.projects.project.host-catalogs.host-catalog.hosts' ); + this.refresh(); + this.notify.success(this.intl.t('notifications.delete-success')); } catch (error) { // TODO: replace with translated strings this.notify.error(error.message, { closeAfter: null }); diff --git a/ui/core/app/routes/scopes/scope/projects/project/targets.js b/ui/core/app/routes/scopes/scope/projects/project/targets.js index fa019bdc7..64f254467 100644 --- a/ui/core/app/routes/scopes/scope/projects/project/targets.js +++ b/ui/core/app/routes/scopes/scope/projects/project/targets.js @@ -2,6 +2,7 @@ import Route from '@ember/routing/route'; import { inject as service } from '@ember/service'; import { action } from '@ember/object'; import loading from 'ember-loading/decorator'; +import { confirm } from '../../../../../utilities/confirm'; export default class ScopesScopeProjectsProjectTargetsRoute extends Route { // =services @@ -62,12 +63,13 @@ export default class ScopesScopeProjectsProjectTargetsRoute extends Route { */ @action @loading + @confirm('questions.delete-confirm') async delete(target) { try { await target.destroyRecord(); + await this.replaceWith('scopes.scope.projects.project.targets'); this.refresh(); this.notify.success(this.intl.t('notifications.delete-success')); - this.transitionTo('scopes.scope.projects.project.targets'); } catch (error) { // TODO: replace with translated strings this.notify.error(error.message, { closeAfter: null }); diff --git a/ui/core/app/routes/scopes/scope/roles.js b/ui/core/app/routes/scopes/scope/roles.js index c9e907069..b7eb93adc 100644 --- a/ui/core/app/routes/scopes/scope/roles.js +++ b/ui/core/app/routes/scopes/scope/roles.js @@ -2,6 +2,7 @@ import Route from '@ember/routing/route'; import { inject as service } from '@ember/service'; import { action } from '@ember/object'; import loading from 'ember-loading/decorator'; +import { confirm } from '../../../utilities/confirm'; export default class ScopesScopeRolesRoute extends Route { // =services @@ -68,12 +69,13 @@ export default class ScopesScopeRolesRoute extends Route { */ @action @loading + @confirm('questions.delete-confirm') async delete(role) { try { await role.destroyRecord(); + await this.replaceWith('scopes.scope.roles'); this.refresh(); this.notify.success(this.intl.t('notifications.delete-success')); - this.transitionTo('scopes.scope.roles'); } catch (error) { // TODO: replace with translated strings this.notify.error(error.message, { closeAfter: null }); diff --git a/ui/core/app/routes/scopes/scope/users.js b/ui/core/app/routes/scopes/scope/users.js index 563cf13d8..5628a5179 100644 --- a/ui/core/app/routes/scopes/scope/users.js +++ b/ui/core/app/routes/scopes/scope/users.js @@ -2,6 +2,7 @@ import Route from '@ember/routing/route'; import { inject as service } from '@ember/service'; import { action } from '@ember/object'; import loading from 'ember-loading/decorator'; +import { confirm } from '../../../utilities/confirm'; export default class ScopesScopeUsersRoute extends Route { // =services @@ -69,12 +70,13 @@ export default class ScopesScopeUsersRoute extends Route { */ @action @loading + @confirm('questions.delete-confirm') async delete(user) { try { await user.destroyRecord(); + await this.replaceWith('scopes.scope.users'); this.refresh(); this.notify.success(this.intl.t('notifications.delete-success')); - this.transitionTo('scopes.scope.users'); } catch (error) { // TODO: replace with translated strings this.notify.error(error.message, { closeAfter: null }); diff --git a/ui/core/app/templates/scopes/scope/auth-methods/auth-method/accounts/account/-actions.hbs b/ui/core/app/templates/scopes/scope/auth-methods/auth-method/accounts/account/-actions.hbs index ea1b0115d..fb179b954 100644 --- a/ui/core/app/templates/scopes/scope/auth-methods/auth-method/accounts/account/-actions.hbs +++ b/ui/core/app/templates/scopes/scope/auth-methods/auth-method/accounts/account/-actions.hbs @@ -2,7 +2,7 @@ {{t 'actions.delete'}} diff --git a/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/-actions.hbs b/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/-actions.hbs index 557d572ba..adbf2705f 100644 --- a/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/-actions.hbs +++ b/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/-actions.hbs @@ -19,4 +19,4 @@ > {{t 'resources.host-catalog.actions.delete'}} - \ No newline at end of file + diff --git a/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/host-sets/-actions.hbs b/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/host-sets/-actions.hbs index 0b6331854..cbf2d45cc 100644 --- a/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/host-sets/-actions.hbs +++ b/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/host-sets/-actions.hbs @@ -5,8 +5,8 @@ {{t 'resources.host-set.actions.delete'}} - \ No newline at end of file + diff --git a/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/-actions.hbs b/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/-actions.hbs index d01333c17..4c9a91aa3 100644 --- a/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/-actions.hbs +++ b/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/-actions.hbs @@ -2,8 +2,8 @@ {{t 'resources.host.actions.delete'}} - \ No newline at end of file + From c3c3f25ea708f9ba15501fd8db2debf34400871d Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Wed, 30 Sep 2020 18:50:12 -0400 Subject: [PATCH 04/14] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20apply=20confirm=20?= =?UTF-8?q?dialog=20to=20remove=20actions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui/core/app/routes/scopes/scope/groups/group/members.js | 4 +++- .../host-catalogs/host-catalog/host-sets/host-set/hosts.js | 2 ++ .../scopes/scope/projects/project/targets/target/host-sets.js | 2 ++ ui/core/app/routes/scopes/scope/roles/role/principals.js | 2 ++ 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/ui/core/app/routes/scopes/scope/groups/group/members.js b/ui/core/app/routes/scopes/scope/groups/group/members.js index 8b3c34bc4..c7f6d52ff 100644 --- a/ui/core/app/routes/scopes/scope/groups/group/members.js +++ b/ui/core/app/routes/scopes/scope/groups/group/members.js @@ -3,6 +3,7 @@ import { action } from '@ember/object'; import { inject as service } from '@ember/service'; import { hash, all } from 'rsvp'; import loading from 'ember-loading/decorator'; +import { confirm } from '../../../../../utilities/confirm'; export default class ScopesScopeGroupsGroupMembersRoute extends Route { // =services @@ -38,10 +39,11 @@ export default class ScopesScopeGroupsGroupMembersRoute extends Route { */ @action @loading + @confirm('questions.remove-confirm') async removeMember(group, member) { try { await group.removeMember(member.id); - await this.refresh(); + this.refresh(); this.notify.success(this.intl.t('notifications.remove-success')); } catch (error) { // TODO: replace with translated strings diff --git a/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/host-sets/host-set/hosts.js b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/host-sets/host-set/hosts.js index e13798b48..de4743209 100644 --- a/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/host-sets/host-set/hosts.js +++ b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/host-sets/host-set/hosts.js @@ -3,6 +3,7 @@ import { hash, all } from 'rsvp'; import { action } from '@ember/object'; import { inject as service } from '@ember/service'; import loading from 'ember-loading/decorator'; +import { confirm } from '../../../../../../../../../utilities/confirm'; export default class ScopesScopeProjectsProjectHostCatalogsHostCatalogHostSetsHostSetHostsRoute extends Route { // =services @@ -37,6 +38,7 @@ export default class ScopesScopeProjectsProjectHostCatalogsHostCatalogHostSetsHo */ @action @loading + @confirm('questions.remove-confirm') async removeHost(hostSet, host) { try { const scopeID = this.modelFor('scopes.scope.projects.project').id; diff --git a/ui/core/app/routes/scopes/scope/projects/project/targets/target/host-sets.js b/ui/core/app/routes/scopes/scope/projects/project/targets/target/host-sets.js index 1633c5713..c87abdf0a 100644 --- a/ui/core/app/routes/scopes/scope/projects/project/targets/target/host-sets.js +++ b/ui/core/app/routes/scopes/scope/projects/project/targets/target/host-sets.js @@ -3,6 +3,7 @@ import { all, hash } from 'rsvp'; import { action } from '@ember/object'; import { inject as service } from '@ember/service'; import loading from 'ember-loading/decorator'; +import { confirm } from '../../../../../../../utilities/confirm'; export default class ScopesScopeProjectsProjectTargetsTargetHostSetsRoute extends Route { // =services @@ -55,6 +56,7 @@ export default class ScopesScopeProjectsProjectTargetsTargetHostSetsRoute extend */ @action @loading + @confirm('questions.remove-confirm') async removeHostSet(target, hostSet) { try { await target.removeHostSet(hostSet.id); diff --git a/ui/core/app/routes/scopes/scope/roles/role/principals.js b/ui/core/app/routes/scopes/scope/roles/role/principals.js index 045d568ce..8fde07b08 100644 --- a/ui/core/app/routes/scopes/scope/roles/role/principals.js +++ b/ui/core/app/routes/scopes/scope/roles/role/principals.js @@ -3,6 +3,7 @@ import { all, hash } from 'rsvp'; import { action } from '@ember/object'; import { inject as service } from '@ember/service'; import loading from 'ember-loading/decorator'; +import { confirm } from '../../../../../utilities/confirm'; export default class ScopesScopeRolesRolePrincipalsRoute extends Route { // =services @@ -65,6 +66,7 @@ export default class ScopesScopeRolesRolePrincipalsRoute extends Route { */ @action @loading + @confirm('questions.remove-confirm') async removePrincipal(principal) { try { const role = this.modelFor('scopes.scope.roles.role'); From 816efec6ed3838b26c29144b3ac771c4f4821efa Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Thu, 1 Oct 2020 14:15:20 -0400 Subject: [PATCH 05/14] =?UTF-8?q?chore:=20=F0=9F=A4=96=20show=20login=20na?= =?UTF-8?q?me=20in=20header?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- addons/auth/addon/authenticators/password.js | 9 +++++++-- ui/core/app/templates/application.hbs | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/addons/auth/addon/authenticators/password.js b/addons/auth/addon/authenticators/password.js index 7403eae65..aa0b13e87 100644 --- a/addons/auth/addon/authenticators/password.js +++ b/addons/auth/addon/authenticators/password.js @@ -93,19 +93,24 @@ export default class PasswordAuthenticator extends BaseAuthenticator { const authEndpointURL = this.buildAuthEndpointURL(options); const response = await fetch(authEndpointURL, { method: 'post', body }); const json = await response.json(); - return response.status < 400 ? resolve(this.normalizeData(json)) : reject(); + return response.status < 400 + ? resolve(this.normalizeData(json, login_name)) + : reject(); } /** * Normalizes the auth data. Adds convenience booleans depending on the * scope within which the session is authenticated: isGlobal, isOrg. + * If a `username` is provided, appends this to the data. * @param {object} data + * @param {string} username * @return {object} */ - normalizeData(data) { + normalizeData(data, username) { // Add booleans indicated the scope type data.isGlobal = (data?.scope?.type === 'global'); data.isOrg = (data?.scope?.type === 'org'); + if (username) data.username = username; return data; } diff --git a/ui/core/app/templates/application.hbs b/ui/core/app/templates/application.hbs index 80472c19a..29dfe48eb 100644 --- a/ui/core/app/templates/application.hbs +++ b/ui/core/app/templates/application.hbs @@ -34,7 +34,7 @@ {{!-- From 9f43a4b276d36596a08ba9acf2f6bd045773e6af Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Thu, 1 Oct 2020 16:19:53 -0400 Subject: [PATCH 06/14] chore: tweak styles to align with design --- addons/rose/addon/styles/rose/components/card/_link.scss | 4 ++++ addons/rose/addon/styles/rose/components/header/_index.scss | 3 ++- addons/rose/addon/styles/rose/variables/color/_css.scss | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/addons/rose/addon/styles/rose/components/card/_link.scss b/addons/rose/addon/styles/rose/components/card/_link.scss index a62df13f8..c31ccbdbd 100644 --- a/addons/rose/addon/styles/rose/components/card/_link.scss +++ b/addons/rose/addon/styles/rose/components/card/_link.scss @@ -1,6 +1,8 @@ @use '../../variables/sizing'; @use '../../utilities/type'; +$xxxs: sizing.rems(xxxs); // 2 + .rose-card-link { @include type.type(s); border: sizing.rems(xxxxs) solid var(--ui-gray-subtler-2); @@ -9,6 +11,8 @@ display: inline-flex; flex-direction: column; margin: 0 sizing.rems(m) sizing.rems(m) 0; + box-shadow: 0 $xxxs $xxxs rgba(var(--stark-components), var(--opacity-4)), + 0 $xxxs $xxxs rgba(var(--stark-components), var(--opacity-4)); a { text-decoration: none; diff --git a/addons/rose/addon/styles/rose/components/header/_index.scss b/addons/rose/addon/styles/rose/components/header/_index.scss index 6ba8aba30..f464a4f80 100644 --- a/addons/rose/addon/styles/rose/components/header/_index.scss +++ b/addons/rose/addon/styles/rose/components/header/_index.scss @@ -12,7 +12,8 @@ display: grid; grid-template-areas: 'brand nav utilities'; grid-template-columns: 0.01fr auto 1fr; - background: var(--black) linear-gradient(90deg, #191a1c 0%, #1b212d 100%); + background: var(--black); + box-shadow: inset 0 -#{sizing.rems(xxxxs)} 0 var(--ui-border-subtler-1); } @media (max-width: media.width(small)) { diff --git a/addons/rose/addon/styles/rose/variables/color/_css.scss b/addons/rose/addon/styles/rose/variables/color/_css.scss index cb000c5df..5c910e93a 100644 --- a/addons/rose/addon/styles/rose/variables/color/_css.scss +++ b/addons/rose/addon/styles/rose/variables/color/_css.scss @@ -83,7 +83,7 @@ @media (prefers-color-scheme: dark) { :root { // poles - --subtle: #{darken(color(ui-gray, 900), 3%)}; + --subtle: var(--black); --stark: #{color(ui-gray, 100)}; --stark-components: 255, 255, 255; From 5200f03417d561cf2a24e8caf42efab3833e4d14 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Thu, 1 Oct 2020 16:31:39 -0400 Subject: [PATCH 07/14] chore: generate more realistic scope IDs --- ui/core/mirage/factories/org.js | 3 --- ui/core/mirage/factories/project.js | 3 --- ui/core/mirage/factories/scope.js | 6 ++++++ 3 files changed, 6 insertions(+), 6 deletions(-) delete mode 100644 ui/core/mirage/factories/org.js delete mode 100644 ui/core/mirage/factories/project.js diff --git a/ui/core/mirage/factories/org.js b/ui/core/mirage/factories/org.js deleted file mode 100644 index fe119cba6..000000000 --- a/ui/core/mirage/factories/org.js +++ /dev/null @@ -1,3 +0,0 @@ -import factory from '../generated/factories/org'; - -export default factory.extend(); diff --git a/ui/core/mirage/factories/project.js b/ui/core/mirage/factories/project.js deleted file mode 100644 index 2a3aae2be..000000000 --- a/ui/core/mirage/factories/project.js +++ /dev/null @@ -1,3 +0,0 @@ -import factory from '../generated/factories/project'; - -export default factory.extend(); diff --git a/ui/core/mirage/factories/scope.js b/ui/core/mirage/factories/scope.js index d60486d15..e008c8249 100644 --- a/ui/core/mirage/factories/scope.js +++ b/ui/core/mirage/factories/scope.js @@ -1,10 +1,16 @@ import factory from '../generated/factories/scope'; import { trait } from 'ember-cli-mirage'; +import { random } from 'faker'; export default factory.extend({ type: 'global', + id() { + const id = new Array(10).fill(0).map(() => random.alphaNumeric()).join(''); + return `s_${id}`; + }, + withChildren: trait({ afterCreate(record, server) { if (record.type === 'global') { From f2d6960ad915981c777ac2d14c036122cf27a4ac Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Thu, 1 Oct 2020 16:40:55 -0400 Subject: [PATCH 08/14] chore: improve loading animation --- addons/rose/addon/styles/rose/components/header/_index.scss | 4 ++++ ui/core/app/styles/app.scss | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/addons/rose/addon/styles/rose/components/header/_index.scss b/addons/rose/addon/styles/rose/components/header/_index.scss index f464a4f80..8885c0207 100644 --- a/addons/rose/addon/styles/rose/components/header/_index.scss +++ b/addons/rose/addon/styles/rose/components/header/_index.scss @@ -14,6 +14,10 @@ grid-template-columns: 0.01fr auto 1fr; background: var(--black); box-shadow: inset 0 -#{sizing.rems(xxxxs)} 0 var(--ui-border-subtler-1); + + @media (prefers-color-scheme: light) { + box-shadow: none; + } } @media (max-width: media.width(small)) { diff --git a/ui/core/app/styles/app.scss b/ui/core/app/styles/app.scss index fbdbea206..052ca77c0 100644 --- a/ui/core/app/styles/app.scss +++ b/ui/core/app/styles/app.scss @@ -76,7 +76,7 @@ &::after { animation: gradient 1s linear infinite; background: repeating-linear-gradient(to right, var(--subtle), var(--subtle), var(--info), var(--subtle), var(--subtle)); - background-position: 0 100%; + background-position: -100% 0; background-size: 200% auto; content: ' '; height: 3px; @@ -86,7 +86,7 @@ top: 100%; @keyframes gradient { - 0% { background-position: 0 0; } + 0% { background-position: -100% 0; } 100% { background-position: -200% 0; } } } From b2052382a871a0f6a814b1c96aa102cd4a0e2df7 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Thu, 1 Oct 2020 16:51:16 -0400 Subject: [PATCH 09/14] fix: mock IDs should be deterministic --- ui/core/mirage/factories/scope.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/ui/core/mirage/factories/scope.js b/ui/core/mirage/factories/scope.js index e008c8249..1b3b868b1 100644 --- a/ui/core/mirage/factories/scope.js +++ b/ui/core/mirage/factories/scope.js @@ -6,9 +6,14 @@ export default factory.extend({ type: 'global', - id() { - const id = new Array(10).fill(0).map(() => random.alphaNumeric()).join(''); - return `s_${id}`; + /** + * Generates realistic-ish IDs while still being deterministic. + */ + id(i) { + let id = 'tv6fv3yz8p'; + if (i % 3) id = 'xk1i4dk50v'; + if (i % 2) id = 's9n819zgko'; + return `s_${id}${i}`; }, withChildren: trait({ From 7a4c6cf5fbb92fdfb708f1be5a49d847846d8dec Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Thu, 1 Oct 2020 17:38:47 -0400 Subject: [PATCH 10/14] chore: swap position of card description and ID --- ui/core/app/templates/scopes/scope/orgs/index.hbs | 4 ++-- ui/core/app/templates/scopes/scope/projects/index.hbs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ui/core/app/templates/scopes/scope/orgs/index.hbs b/ui/core/app/templates/scopes/scope/orgs/index.hbs index ed79b4e04..f073d3dcf 100644 --- a/ui/core/app/templates/scopes/scope/orgs/index.hbs +++ b/ui/core/app/templates/scopes/scope/orgs/index.hbs @@ -30,8 +30,8 @@ {{#each @model as |org|}} diff --git a/ui/core/app/templates/scopes/scope/projects/index.hbs b/ui/core/app/templates/scopes/scope/projects/index.hbs index 0d05ddf30..5995e29fe 100644 --- a/ui/core/app/templates/scopes/scope/projects/index.hbs +++ b/ui/core/app/templates/scopes/scope/projects/index.hbs @@ -30,8 +30,8 @@ {{#each @model as |project|}} From 362d31ec4014ac227fdb144a0f08a5ad8a03e333 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Thu, 1 Oct 2020 17:42:55 -0400 Subject: [PATCH 11/14] =?UTF-8?q?chore:=20=F0=9F=A4=96=20prevent=20unneces?= =?UTF-8?q?sary=20auth-method=20loading?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit which prevents unnecessary loading indicator jank --- .../app/routes/scopes/scope/authenticate.js | 22 +++++++++---------- ui/core/mirage/factories/scope.js | 1 - 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/ui/core/app/routes/scopes/scope/authenticate.js b/ui/core/app/routes/scopes/scope/authenticate.js index d011a6b45..883bbb704 100644 --- a/ui/core/app/routes/scopes/scope/authenticate.js +++ b/ui/core/app/routes/scopes/scope/authenticate.js @@ -9,6 +9,17 @@ export default class ScopesScopeAuthenticateRoute extends Route { // =methods + beforeModel() { + if (this.session.isAuthenticated) { + const scope = this.modelFor('scopes.scope'); + if (scope.isGlobal) { + this.transitionTo('scopes.scope.orgs'); + } else { + this.transitionTo('scopes.scope.projects'); + } + } + } + /** * Returns all auth methods for the current scope, along with the current * scope and all scopes (for org navigation). @@ -28,15 +39,4 @@ export default class ScopesScopeAuthenticateRoute extends Route { // }]) }); } - - redirect() { - if (this.session.isAuthenticated) { - const scope = this.modelFor('scopes.scope'); - if (scope.isGlobal) { - this.transitionTo('scopes.scope.orgs'); - } else { - this.transitionTo('scopes.scope.projects'); - } - } - } } diff --git a/ui/core/mirage/factories/scope.js b/ui/core/mirage/factories/scope.js index 1b3b868b1..a14dbc719 100644 --- a/ui/core/mirage/factories/scope.js +++ b/ui/core/mirage/factories/scope.js @@ -1,6 +1,5 @@ import factory from '../generated/factories/scope'; import { trait } from 'ember-cli-mirage'; -import { random } from 'faker'; export default factory.extend({ From 50b2a9cc8eab7d52a24be9b063f45fe3acd33437 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Thu, 1 Oct 2020 17:46:23 -0400 Subject: [PATCH 12/14] style: tweak sidebar selection style to be more apparent --- addons/rose/addon/styles/rose/components/nav/_sidebar.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/rose/addon/styles/rose/components/nav/_sidebar.scss b/addons/rose/addon/styles/rose/components/nav/_sidebar.scss index 101f3753f..fcc29ca45 100644 --- a/addons/rose/addon/styles/rose/components/nav/_sidebar.scss +++ b/addons/rose/addon/styles/rose/components/nav/_sidebar.scss @@ -39,6 +39,7 @@ &.active { color: var(--action); border-color: currentColor; + background-color: var(--action-subtler-1h); } &:hover { From 731de5b7cb0fd8bf87a037f71311f2d8b4ab1866 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Thu, 1 Oct 2020 18:59:31 -0400 Subject: [PATCH 13/14] =?UTF-8?q?chore:=20=F0=9F=A4=96=20enhance=20card=20?= =?UTF-8?q?link=20component=20to=20better=20match=20design?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../addon/components/rose/card/link/index.hbs | 31 +++++-- .../styles/rose/components/card/_link.scss | 93 +++++++++++++++---- .../components/rose/card/link-test.js | 29 +++--- .../app/templates/scopes/scope/orgs/index.hbs | 5 +- .../templates/scopes/scope/projects/index.hbs | 5 +- 5 files changed, 118 insertions(+), 45 deletions(-) diff --git a/addons/rose/addon/components/rose/card/link/index.hbs b/addons/rose/addon/components/rose/card/link/index.hbs index 7f003a4a6..ead6f0ee9 100644 --- a/addons/rose/addon/components/rose/card/link/index.hbs +++ b/addons/rose/addon/components/rose/card/link/index.hbs @@ -4,17 +4,30 @@ @route={{if @route @route "index"}} @model={{@model}} > - {{#if @title}} - - {{/if}} + - {{#if @subtitle}} - - {{/if}} + - {{#if @description}} - - {{/if}} + {{yield}} diff --git a/addons/rose/addon/styles/rose/components/card/_link.scss b/addons/rose/addon/styles/rose/components/card/_link.scss index c31ccbdbd..ad33c49c5 100644 --- a/addons/rose/addon/styles/rose/components/card/_link.scss +++ b/addons/rose/addon/styles/rose/components/card/_link.scss @@ -1,4 +1,6 @@ +@use 'sass:map'; @use '../../variables/sizing'; +@use '../../variables/typography'; @use '../../utilities/type'; $xxxs: sizing.rems(xxxs); // 2 @@ -14,18 +16,18 @@ $xxxs: sizing.rems(xxxs); // 2 box-shadow: 0 $xxxs $xxxs rgba(var(--stark-components), var(--opacity-4)), 0 $xxxs $xxxs rgba(var(--stark-components), var(--opacity-4)); + @media (prefers-color-scheme: dark) { + background-color: var(--ui-gray-subtler-6); + } + a { text-decoration: none; margin: 0; padding: 0; - display: flex; - justify-content: space-between; - flex-direction: column; height: 100%; - padding: sizing.rems(s); // TODO: Figure out better method of defining card dimensions - min-height: sizing.rems(m) * 6; //6 rems - width: sizing.rems(m) * 12; //12 rems + min-height: sizing.rems(m) * 8; //6 rems + width: sizing.rems(m) * 16; //12 rems } &:hover { @@ -46,27 +48,78 @@ $xxxs: sizing.rems(xxxs); // 2 border-color: var(--info); } - .rose-card-link-title { - $truncate-line-count: 3; - @include type.type(m, semibold); - color: var(--stark); - margin: 0; - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: $truncate-line-count; - overflow: hidden; + .rose-card-link-header, + .rose-card-link-body, + .rose-card-link-footer { + padding-left: sizing.rems(m); + padding-right: sizing.rems(m); } - .rose-card-link-subtitle { - color: var(--ui-gray); + .rose-card-link-header { + padding-top: sizing.rems(m); + } + .rose-card-link-footer { + margin-top: sizing.rems(xs) + sizing.rems(xxs); + padding-top: sizing.rems(xs) * 2; + padding-bottom: sizing.rems(xs) * 2; + } + + .rose-card-link-footer { + border-top: sizing.rems(xxxxs) solid var(--ui-gray-subtler-2); + } + + .rose-card-link-title { + @include type.type(m, semibold); + display: flex; + color: var(--stark); margin: 0; + margin-bottom: sizing.rems(xs); + + .rose-icon { + margin-right: sizing.rems(xs); + + svg { + vertical-align: text-bottom; + } + } + + .rose-card-link-title-text { + flex-grow: 1; + height: typography.line-height(m) * 2rem; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; + } } .rose-card-link-description { - margin: auto 0 0; color: var(--ui-gray); - white-space: nowrap; + margin: 0; + height: typography.line-height(m) * 2rem; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; overflow: hidden; - text-overflow: ellipsis; } + + .rose-card-link-footer-id { + @include type.type(xs); + color: var(--ui-gray); + margin: 0; + + &::before { + content: "ID"; + color: var(--stark); + padding-right: sizing.rems(xs); + } + } + + // .rose-card-link-description { + // margin: auto 0 0; + // color: var(--ui-gray); + // white-space: nowrap; + // overflow: hidden; + // text-overflow: ellipsis; + // } } diff --git a/addons/rose/tests/integration/components/rose/card/link-test.js b/addons/rose/tests/integration/components/rose/card/link-test.js index 78a02af4b..288679791 100644 --- a/addons/rose/tests/integration/components/rose/card/link-test.js +++ b/addons/rose/tests/integration/components/rose/card/link-test.js @@ -10,9 +10,9 @@ module('Integration | Component | rose/card/link', function (hooks) { await render(hbs``); assert.ok(find('article')); assert.ok(find('.rose-card-link')); - assert.notOk(find('.rose-card-link-title')); - assert.notOk(find('.rose-card-link-subtitle')); - assert.notOk(find('.rose-card-link-description')); + assert.ok(find('.rose-card-link-header')); + assert.ok(find('.rose-card-link-body')); + assert.ok(find('.rose-card-link-footer')); }); test('it renders with @title', async function (assert) { @@ -24,22 +24,27 @@ module('Integration | Component | rose/card/link', function (hooks) { ); }); - test('it renders with @subtitle', async function (assert) { - await render(hbs``); - assert.equal( - find('.rose-card-link-subtitle').textContent.trim(), - 'card subtitle' - ); - }); - test('it renders with @description', async function (assert) { await render(hbs``); assert.equal( - find('.rose-card-link-description').textContent.trim(), + find('.rose-card-link-body').textContent.trim(), 'card description' ); }); + test('it renders with @id', async function (assert) { + await render(hbs``); + assert.equal( + find('.rose-card-link-footer-id').textContent.trim(), + 'card_id' + ); + }); + + test('it renders with @icon', async function (assert) { + await render(hbs``); + assert.ok(find('.rose-icon')); + }); + test('it renders with attributes', async function (assert) { await render(hbs``); assert.ok(find('#card')); diff --git a/ui/core/app/templates/scopes/scope/orgs/index.hbs b/ui/core/app/templates/scopes/scope/orgs/index.hbs index f073d3dcf..30c211450 100644 --- a/ui/core/app/templates/scopes/scope/orgs/index.hbs +++ b/ui/core/app/templates/scopes/scope/orgs/index.hbs @@ -30,8 +30,9 @@ {{#each @model as |org|}} diff --git a/ui/core/app/templates/scopes/scope/projects/index.hbs b/ui/core/app/templates/scopes/scope/projects/index.hbs index 5995e29fe..6173aeffb 100644 --- a/ui/core/app/templates/scopes/scope/projects/index.hbs +++ b/ui/core/app/templates/scopes/scope/projects/index.hbs @@ -30,8 +30,9 @@ {{#each @model as |project|}} From 81dbade33b91c51426483141468bbddc17d493cf Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Thu, 1 Oct 2020 21:45:25 -0400 Subject: [PATCH 14/14] chore: Rename lable to labeler for github action --- .github/{labler.yml => labeler.yml} | 0 .github/workflows/{labler.yml => labeler.yml} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename .github/{labler.yml => labeler.yml} (100%) rename .github/workflows/{labler.yml => labeler.yml} (100%) diff --git a/.github/labler.yml b/.github/labeler.yml similarity index 100% rename from .github/labler.yml rename to .github/labeler.yml diff --git a/.github/workflows/labler.yml b/.github/workflows/labeler.yml similarity index 100% rename from .github/workflows/labler.yml rename to .github/workflows/labeler.yml