From 85920aaca471e8ec32857bc1fa0b14404a65bf7f Mon Sep 17 00:00:00 2001 From: AndriiMysko Date: Wed, 8 Feb 2023 17:33:03 +0200 Subject: [PATCH 01/43] New payment details implementation --- app/components/billing/payment-details-tab.js | 165 + app/components/profile-nav.js | 14 + app/controllers/account/payment_details.js | 4 + .../organization/payment_details.js | 4 + app/mixins/controller/payment_details.js | 10 + app/mixins/route/account/payment_details.js | 10 + app/models/allowance.js | 4 + app/router.js | 1 + app/routes/account/payment_details.js | 11 + app/styles/app/layouts/billing.scss | 22 + app/templates/account/payment_details.hbs | 5 + app/templates/components/billing/account.hbs | 17 - app/templates/components/billing/invoices.hbs | 2 +- .../billing/payment-details-tab.hbs | 226 ++ .../components/billing/summary-v2.hbs | 7 - .../flashes/payment-details-edit-lock.hbs | 9 + app/templates/components/profile-nav.hbs | 15 +- config/environment.js | 5 + package-lock.json | 2715 ++++++++++++++++- package.json | 1 + 20 files changed, 3054 insertions(+), 193 deletions(-) create mode 100644 app/components/billing/payment-details-tab.js create mode 100644 app/controllers/account/payment_details.js create mode 100644 app/controllers/organization/payment_details.js create mode 100644 app/mixins/controller/payment_details.js create mode 100644 app/mixins/route/account/payment_details.js create mode 100644 app/routes/account/payment_details.js create mode 100644 app/templates/account/payment_details.hbs create mode 100644 app/templates/components/billing/payment-details-tab.hbs create mode 100644 app/templates/components/flashes/payment-details-edit-lock.hbs diff --git a/app/components/billing/payment-details-tab.js b/app/components/billing/payment-details-tab.js new file mode 100644 index 0000000000..538d872338 --- /dev/null +++ b/app/components/billing/payment-details-tab.js @@ -0,0 +1,165 @@ +import Component from '@ember/component'; +import { task } from 'ember-concurrency'; +import { inject as service } from '@ember/service'; +import { empty, not, reads, and } from '@ember/object/computed'; +import { computed } from '@ember/object'; +import config from 'travis/config/environment'; +import { underscore } from '@ember/string'; +import { countries, states, stateCountries, nonZeroVatThresholdCountries, zeroVatThresholdCountries } from 'travis/utils/countries'; + +export default Component.extend({ + api: service(), + stripe: service(), + store: service(), + flashes: service(), + metrics: service(), + + countries, + states: computed('country', function () { + const { country } = this; + + return states[country]; + }), + account: null, + stripeElement: null, + stripeLoading: false, + couponId: null, + options: computed('disableForm', function () { + let configStripe = config.stripeOptions; + configStripe['disabled'] = this.get('disableForm'); + return configStripe; + }), + showSwitchToFreeModal: false, + showPlanSwitchWarning: false, + + subscription: reads('account.subscription'), + v2subscription: reads('account.v2subscription'), + isV2SubscriptionEmpty: empty('v2subscription'), + isSubscriptionEmpty: empty('subscription'), + isSubscriptionsEmpty: and('isSubscriptionEmpty', 'isV2SubscriptionEmpty'), + hasV2Subscription: not('isV2SubscriptionEmpty'), + invoices: computed('subscription.id', 'v2subscription.id', function () { + const subscriptionId = this.isV2SubscriptionEmpty ? this.get('subscription.id') : this.get('v2subscription.id'); + const type = this.isV2SubscriptionEmpty ? 1 : 2; + if (subscriptionId) { + return this.store.query('invoice', { type, subscriptionId }); + } else { + return []; + } + }), + + disableForm: computed('account.allowance.paymentChangesBlockCredit', 'account.allowance.paymentChangesBlockCaptcha', function () { + const paymentChangesBlockCredit = this.account.allowance.get('paymentChangesBlockCredit'); + const paymentChangesBlockCaptcha = this.account.allowance.get('paymentChangesBlockCaptcha'); + return paymentChangesBlockCaptcha || paymentChangesBlockCredit; + }), + + billingInfo: reads('v2subscription.billingInfo'), + + country: reads('billingInfo.country'), + firstName: reads('billingInfo.firstName'), + lastName: reads('billingInfo.lastName'), + nameOnCard: computed('firstName', 'lastName', function () { + return `${this.firstName} ${this.lastName}`; + }), + + isLoading: reads('updatePaymentDetails.isRunning'), + + updatePaymentDetails: task(function* (reCaptchaResponse) { + this.metrics.trackEvent({ + action: 'Pay Button Clicked', + category: 'Subscription', + }); + const { stripeElement } = this; + const subscription = this.v2subscription; + try { + let token = null; + if (stripeElement) { + let res = yield this.stripe.createStripeToken.perform(stripeElement); + token = res.token; + } + const changedInfoAttrs = this.billingInfo.changedAttributes(); + let paymentDetails = {}; + paymentDetails['captcha_token'] = reCaptchaResponse; + Object.keys(changedInfoAttrs).forEach(key => { + paymentDetails[underscore(key)] = changedInfoAttrs[key][1]; + }); + if (token) { + paymentDetails['token'] = token.id; + } + yield this.api.patch(`/v2_subscription/${subscription.id}/payment_details`, { + data: paymentDetails + }); + if (stripeElement) { + this.stripeElement.clear(); + this.set('stripeElement', null); + } + this.flashes.success('Successfully updated payment information.'); + this.billingInfo.save(); + } catch (error) { + if (typeof(error.json) === 'function') { + const err = yield error.json(); + this.account.allowance.reload(); + this.flashes.error(err['error_message']); + } + } + }).drop(), + + isZeroVatThresholdCountry: computed('country', function () { + const { country } = this; + return !!country && zeroVatThresholdCountries.includes(country); + }), + + isNonZeroVatThresholdCountry: computed('country', function () { + const { country } = this; + return !!country && nonZeroVatThresholdCountries.includes(country); + }), + + isStateCountry: computed('country', function () { + const { country } = this; + + return !!country && stateCountries.includes(country); + }), + + isVatMandatory: computed('isNonZeroVatThresholdCountry', 'hasLocalRegistration', function () { + const { isNonZeroVatThresholdCountry, isZeroVatThresholdCountry, hasLocalRegistration } = this; + return isZeroVatThresholdCountry || (isNonZeroVatThresholdCountry ? hasLocalRegistration : false); + }), + + showNonZeroVatConfirmation: reads('isNonZeroVatThresholdCountry'), + + showVatField: computed('country', 'isNonZeroVatThresholdCountry', 'hasLocalRegistration', function () { + const { country, isNonZeroVatThresholdCountry, hasLocalRegistration } = this; + return country && (isNonZeroVatThresholdCountry ? hasLocalRegistration : true); + }), + + isStateMandatory: reads('isStateCountry'), + + enableSubmit: computed('stripeElement', 'billingInfo.hasDirtyAttributes', function () { + return this.stripeElement || (this.billingInfo && this.billingInfo.hasDirtyAttributes); + }), + + actions: { + complete(stripeElement) { + if (!this.enableSubmit || this.disableForm) { + stripeElement.clear(); + return; + } + this.set('stripeElement', stripeElement); + }, + modifyNameOnCard(value) { + this.set('nameOnCard', value); + this.billingInfo.setProperties({ + firstName: this.nameOnCard.split(' ')[0], + lastName: this.nameOnCard.split(' ')[1] + }); + }, + onCaptchaResolved(reCaptchaResponse) { + this.updatePaymentDetails.perform(reCaptchaResponse); + }, + submit() { + if (!this.enableSubmit || this.disableForm) return; + window.grecaptcha.execute(); + } + } +}); diff --git a/app/components/profile-nav.js b/app/components/profile-nav.js index 9bcff85772..3302218525 100644 --- a/app/components/profile-nav.js +++ b/app/components/profile-nav.js @@ -68,6 +68,13 @@ export default Component.extend({ const isEnterprise = this.features.get('enterpriseVersion'); return !isEnterprise && !isAssemblaUser && !!billingEndpoint; }), + showPaymentDetailsTab: computed('showSubscriptionTab', 'isOrganization', 'isOrganizationAdmin', function () { + if (this.isOrganization) { + return this.showSubscriptionTab && this.isOrganizationAdmin; + } else { + return this.showSubscriptionTab; + } + }), showPlanUsageTab: and('showSubscriptionTab', 'model.hasCredits'), usersUsage: computed('account.allowance.userUsage', 'addonUsage', function () { const userUsage = this.model.allowance.get('userUsage'); @@ -88,6 +95,13 @@ export default Component.extend({ return; } + if (allowance.get('paymentChangesBlockCredit') || allowance.get('paymentChangesBlockCaptcha')) { + let time; + if (allowance.get('paymentChangesBlockCaptcha')) time = allowance.get('captchaBlockDuration'); + if (allowance.get('paymentChangesBlockCredit')) time = allowance.get('creditCardBlockDuration'); + this.flashes.custom('flashes/payment-details-edit-lock', { owner: this.model, isUser: this.model.isUser, time: time}, 'warning'); + } + if (allowance.get('subscriptionType') !== 2) { return; } diff --git a/app/controllers/account/payment_details.js b/app/controllers/account/payment_details.js new file mode 100644 index 0000000000..17f0c437d4 --- /dev/null +++ b/app/controllers/account/payment_details.js @@ -0,0 +1,4 @@ +import Controller from '@ember/controller'; +import PaymentDetailsControllerMixin from 'travis/mixins/controller/payment_details'; + +export default Controller.extend(PaymentDetailsControllerMixin, {}); diff --git a/app/controllers/organization/payment_details.js b/app/controllers/organization/payment_details.js new file mode 100644 index 0000000000..17f0c437d4 --- /dev/null +++ b/app/controllers/organization/payment_details.js @@ -0,0 +1,4 @@ +import Controller from '@ember/controller'; +import PaymentDetailsControllerMixin from 'travis/mixins/controller/payment_details'; + +export default Controller.extend(PaymentDetailsControllerMixin, {}); diff --git a/app/mixins/controller/payment_details.js b/app/mixins/controller/payment_details.js new file mode 100644 index 0000000000..c756cde18a --- /dev/null +++ b/app/mixins/controller/payment_details.js @@ -0,0 +1,10 @@ +import Mixin from '@ember/object/mixin'; +import { reads } from '@ember/object/computed'; +import { inject as service } from '@ember/service'; + +export default Mixin.create({ + storage: service(), + + account: reads('model.account'), + subscription: reads('account.v2subscription') +}); diff --git a/app/mixins/route/account/payment_details.js b/app/mixins/route/account/payment_details.js new file mode 100644 index 0000000000..e71c71d7d6 --- /dev/null +++ b/app/mixins/route/account/payment_details.js @@ -0,0 +1,10 @@ +import Mixin from '@ember/object/mixin'; +import { inject as service } from '@ember/service'; + +export default Mixin.create({ + stripe: service(), + + beforeModel() { + return this.stripe.load(); + } +}); diff --git a/app/models/allowance.js b/app/models/allowance.js index a1ebe39fd5..664f7ca7ee 100644 --- a/app/models/allowance.js +++ b/app/models/allowance.js @@ -7,6 +7,10 @@ export default Model.extend({ userUsage: attr('boolean'), pendingUserLicenses: attr('boolean'), concurrencyLimit: attr('number'), + paymentChangesBlockCredit: attr('boolean'), + paymentChangesBlockCaptcha: attr('boolean'), + creditCardBlockDuration: attr('number'), + captchaBlockDuration: attr('number'), owner: belongsTo('owner') }); diff --git a/app/router.js b/app/router.js index f44fb47844..46441041e2 100644 --- a/app/router.js +++ b/app/router.js @@ -41,6 +41,7 @@ Router.map(function () { this.route('settings', { path: '/preferences' }); this.route('billing', { path: '/plan' }); this.route('plan_usage', { path: '/plan/usage' }); + this.route('payment_details', { path: '/payment-details' }); this.route('migrate'); }); this.route('organization', { path: '/organizations/:login' }, function () { diff --git a/app/routes/account/payment_details.js b/app/routes/account/payment_details.js new file mode 100644 index 0000000000..d9341c8c08 --- /dev/null +++ b/app/routes/account/payment_details.js @@ -0,0 +1,11 @@ +import TravisRoute from 'travis/routes/basic'; +import AccountPaymentDetailsMixin from 'travis/mixins/route/account/payment_details'; +import { hash } from 'rsvp'; + +export default TravisRoute.extend(AccountPaymentDetailsMixin, { + model() { + return hash({ + account: this.modelFor('account'), + }); + } +}); diff --git a/app/styles/app/layouts/billing.scss b/app/styles/app/layouts/billing.scss index 0c1043a68d..0d9b1e6e37 100644 --- a/app/styles/app/layouts/billing.scss +++ b/app/styles/app/layouts/billing.scss @@ -1068,6 +1068,28 @@ } } } + + .payment-details-label { + color: $cement-grey; + display: block; + text-transform: uppercase; + font-weight: 600; + font-size: 0.9em; + margin-bottom: 5px; + + .required { + color: $brick-red; + } + } + + .card-field { + margin-bottom: 2rem; + } + + .payment-details-form { + padding-bottom: 3rem; + border-bottom: solid 2px #f1f1f1; + } } .user-management-modal { diff --git a/app/templates/account/payment_details.hbs b/app/templates/account/payment_details.hbs new file mode 100644 index 0000000000..c3658366a6 --- /dev/null +++ b/app/templates/account/payment_details.hbs @@ -0,0 +1,5 @@ +
+ +
diff --git a/app/templates/components/billing/account.hbs b/app/templates/components/billing/account.hbs index 29f185b572..de10c7deab 100644 --- a/app/templates/components/billing/account.hbs +++ b/app/templates/components/billing/account.hbs @@ -35,21 +35,4 @@ /> {{/if}} - {{#if this.showInvoices}} - {{#if this.hasV2Subscription}} - {{#if this.v2subscription.isNotManual}} - - {{/if}} - {{else}} - - {{/if}} - {{/if}} {{/if}} diff --git a/app/templates/components/billing/invoices.hbs b/app/templates/components/billing/invoices.hbs index 8af90d54ef..054044c673 100644 --- a/app/templates/components/billing/invoices.hbs +++ b/app/templates/components/billing/invoices.hbs @@ -1,5 +1,5 @@
-

Invoice history

+

Payment history

diff --git a/app/templates/components/billing/payment-details-tab.hbs b/app/templates/components/billing/payment-details-tab.hbs new file mode 100644 index 0000000000..4cd3d65aab --- /dev/null +++ b/app/templates/components/billing/payment-details-tab.hbs @@ -0,0 +1,226 @@ +

+

+ You can update your payment method details here. Your credit card will be validated + by placing test fee - $1, which will be returned to you within a week. +

+ +
+ + + + + + + credit card details + + * + + + + + {{#if stripeError}} +

{{stripeError.message}}

+ {{/if}} +
+ + + + + + + +
 
+
+ + + + + +
+
+ + + +
+
+ + + +
+
+
+
+ + + {{country}} + + +
+ {{#if this.isStateMandatory}} +
+ + + {{state}} + + +
+ {{/if}} +
+ + {{#if this.showNonZeroVatConfirmation}} +
+
+ Is your company registered locally for VAT/GST? +
+
+ + Yes + +
+
+ + No + +
+
+ {{/if}} + {{#if this.showVatField}} +
+ + + +
+ {{/if}} + +
+ {{#if this.isLoading}} + + {{else}} + {{g-recaptcha onSuccess=(action "onCaptchaResolved") size="invisible"}} + + {{/if}} +
+
+
+ + {{#if this.invoices}} + {{#if this.hasV2Subscription}} + {{#if this.v2subscription.isNotManual}} + + {{/if}} + {{else}} + + {{/if}} + {{/if}} +
diff --git a/app/templates/components/billing/summary-v2.hbs b/app/templates/components/billing/summary-v2.hbs index 0f207b7e7a..b46755c7dd 100644 --- a/app/templates/components/billing/summary-v2.hbs +++ b/app/templates/components/billing/summary-v2.hbs @@ -272,13 +272,6 @@ {{#if this.subscription.hasCredits}} {{/if}} - {{#if (not this.subscription.plan.isFree)}} - - {{#if this.subscription.isNotManual}} - - {{/if}} -
- {{/if}} {{/if}} {{#if this.subscription.isManual}} diff --git a/app/templates/components/flashes/payment-details-edit-lock.hbs b/app/templates/components/flashes/payment-details-edit-lock.hbs new file mode 100644 index 0000000000..bf3279e156 --- /dev/null +++ b/app/templates/components/flashes/payment-details-edit-lock.hbs @@ -0,0 +1,9 @@ +
    +
  • +

    + + You reached the maximum number of tries. The payment details edition is blocked for {{@data.time}} hours. Please, try again later. + +

    +
  • +
\ No newline at end of file diff --git a/app/templates/components/profile-nav.hbs b/app/templates/components/profile-nav.hbs index e1bdc8884c..2b78537a7f 100644 --- a/app/templates/components/profile-nav.hbs +++ b/app/templates/components/profile-nav.hbs @@ -112,6 +112,13 @@ Settings + {{#if this.showMigrateTab}} +
  • + + Migrate + +
  • + {{/if}} {{#if this.showSubscriptionTab}}
  • @@ -119,10 +126,10 @@
  • {{/if}} - {{#if this.showMigrateTab}} -
  • - - Migrate + {{#if this.showPaymentDetailsTab}} +
  • + + Payment details
  • {{/if}} diff --git a/config/environment.js b/config/environment.js index cb80ff2ea0..eb84ea17a4 100644 --- a/config/environment.js +++ b/config/environment.js @@ -18,6 +18,7 @@ const { GOOGLE_ANALYTICS_ID, GOOGLE_TAGS_CONTAINER_ID, GOOGLE_TAGS_PARAMS, + GOOGLE_RECAPTCHA_SITE_KEY, STRIPE_PUBLISHABLE_KEY, GITHUB_APPS_APP_NAME, API_ENDPOINT, @@ -163,6 +164,10 @@ module.exports = function (environment) { } } }, + + gReCaptcha: { + siteKey: GOOGLE_RECAPTCHA_SITE_KEY + }, }; ENV.metricsAdapters = []; diff --git a/package-lock.json b/package-lock.json index 089584e9fb..760874b01a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1107,6 +1107,16 @@ } } }, + "@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "dev": true, + "requires": { + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, "@babel/code-frame": { "version": "7.5.5", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", @@ -1384,6 +1394,127 @@ "lodash": "^4.17.13" } }, + "@babel/helper-define-polyfill-provider": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz", + "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==", + "dev": true, + "requires": { + "@babel/helper-compilation-targets": "^7.17.7", + "@babel/helper-plugin-utils": "^7.16.7", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2", + "semver": "^6.1.2" + }, + "dependencies": { + "@babel/compat-data": { + "version": "7.20.14", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.14.tgz", + "integrity": "sha512-0YpKHD6ImkWMEINCyDAD0HLLUH/lPCefG8ld9it8DJB2wnApraKuhgYTvTY1z7UFIfBTGy5LwncZ+5HWWGbhFw==", + "dev": true + }, + "@babel/helper-compilation-targets": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz", + "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.20.5", + "@babel/helper-validator-option": "^7.18.6", + "browserslist": "^4.21.3", + "lru-cache": "^5.1.1", + "semver": "^6.3.0" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + }, + "browserslist": { + "version": "4.21.5", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", + "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001449", + "electron-to-chromium": "^1.4.284", + "node-releases": "^2.0.8", + "update-browserslist-db": "^1.0.10" + } + }, + "caniuse-lite": { + "version": "1.0.30001450", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001450.tgz", + "integrity": "sha512-qMBmvmQmFXaSxexkjjfMvD5rnDL0+m+dUMZKoDYsGG8iZN29RuYh9eRoMvKsT6uMAWlyUUGDEQGJJYjzCIO9ew==", + "dev": true + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "electron-to-chromium": { + "version": "1.4.288", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.288.tgz", + "integrity": "sha512-8s9aJf3YiokIrR+HOQzNOGmEHFXVUQzXM/JaViVvKdCkNUjS+lEa/uT7xw3nDVG/IgfxiIwUGkwJ6AR1pTpYsQ==", + "dev": true + }, + "lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node-releases": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", + "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", + "dev": true + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dev": true, + "requires": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@babel/helper-environment-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", + "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "dev": true + }, "@babel/helper-explode-assignable-expression": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.1.0.tgz", @@ -1552,11 +1683,23 @@ "@babel/types": "^7.4.4" } }, + "@babel/helper-string-parser": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", + "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", + "dev": true + }, "@babel/helper-validator-identifier": { "version": "7.9.5", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz", "integrity": "sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g==" }, + "@babel/helper-validator-option": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", + "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "dev": true + }, "@babel/helper-wrap-function": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.2.0.tgz", @@ -1594,6 +1737,79 @@ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.6.4.tgz", "integrity": "sha512-D8RHPW5qd0Vbyo3qb+YjO5nvUVRTXFLQ/FsDxJU2Nqz4uB5EnUN0ZQSEYpvTIbRuttig1XbHWU5oMeQwQSAA+A==" }, + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz", + "integrity": "sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.20.7.tgz", + "integrity": "sha512-sbr9+wNE5aXMBBFBICk01tt7sBf2Oc9ikRFEcem/ZORup9IMUdNhW7/wVLEbbtlWOsEubJet46mHAL2C8+2jKQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", + "@babel/plugin-proposal-optional-chaining": "^7.20.7" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + }, + "@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz", + "integrity": "sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==", + "dev": true, + "requires": { + "@babel/types": "^7.20.0" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true + }, + "@babel/plugin-proposal-optional-chaining": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.20.7.tgz", + "integrity": "sha512-T+A7b1kfjtRM51ssoOfS1+wbyCVqorfyZhT99TvxxLMirPShD8CzKMRepMlCBGM5RpHMbn8s+5MMHnPstJH6mQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + } + }, + "@babel/types": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.7.tgz", + "integrity": "sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + } + } + } + }, "@babel/plugin-proposal-async-generator-functions": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.2.0.tgz", @@ -1615,6 +1831,228 @@ "@babel/helper-plugin-utils": "^7.0.0" } }, + "@babel/plugin-proposal-class-static-block": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.20.7.tgz", + "integrity": "sha512-AveGOoi9DAjUYYuUAG//Ig69GlazLnoyzMw68VCDux+c1tsnnH/OkYcpz/5xzMkEFC6UxjR5Gw1c+iY2wOGVeQ==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.20.7", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "dev": true, + "requires": { + "@babel/highlight": "^7.18.6" + } + }, + "@babel/generator": { + "version": "7.20.14", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.14.tgz", + "integrity": "sha512-AEmuXHdcD3A52HHXxaTmYlb8q/xMEhoRP67B3T4Oq7lbmSoqroMZzjnGj3+i1io3pdnF8iBYVu4Ilj+c4hBxYg==", + "dev": true, + "requires": { + "@babel/types": "^7.20.7", + "@jridgewell/gen-mapping": "^0.3.2", + "jsesc": "^2.5.1" + } + }, + "@babel/helper-annotate-as-pure": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", + "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-create-class-features-plugin": { + "version": "7.20.12", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.20.12.tgz", + "integrity": "sha512-9OunRkbT0JQcednL0UFvbfXpAsUXiGjUk0a7sN8fUXX7Mue79cUSMjHGDRRi/Vz9vYlpIhLV5fMD5dKoMhhsNQ==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-member-expression-to-functions": "^7.20.7", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-replace-supers": "^7.20.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", + "@babel/helper-split-export-declaration": "^7.18.6" + } + }, + "@babel/helper-function-name": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", + "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", + "dev": true, + "requires": { + "@babel/template": "^7.18.10", + "@babel/types": "^7.19.0" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.20.7.tgz", + "integrity": "sha512-9J0CxJLq315fEdi4s7xK5TQaNYjZw+nDVpVqr1axNGKzdrdwYBD5b4uKv3n75aABG0rCCTK8Im8Ww7eYfMrZgw==", + "dev": true, + "requires": { + "@babel/types": "^7.20.7" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz", + "integrity": "sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + }, + "@babel/helper-replace-supers": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.20.7.tgz", + "integrity": "sha512-vujDMtB6LVfNW13jhlCrp48QNslK6JXi7lQG736HVbHz/mbf4Dc7tIRh1Xf5C0rF7BP8iiSxGMCmY6Ci1ven3A==", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-member-expression-to-functions": "^7.20.7", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.20.7", + "@babel/types": "^7.20.7" + } + }, + "@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz", + "integrity": "sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==", + "dev": true, + "requires": { + "@babel/types": "^7.20.0" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true + }, + "@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.20.15", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.15.tgz", + "integrity": "sha512-DI4a1oZuf8wC+oAJA9RW6ga3Zbe8RZFt7kD9i4qAspz3I/yHet1VvC3DiSy/fsUvv5pvJuNPh0LPOdCcqinDPg==", + "dev": true + }, + "@babel/template": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", + "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7" + } + }, + "@babel/traverse": { + "version": "7.20.13", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.13.tgz", + "integrity": "sha512-kMJXfF0T6DIS9E8cgdLCSAL+cuCK+YEZHWiLK0SXpTo8YRj5lpJu3CDNKiIBCne4m9hhTIqUg6SYTAI39tAiVQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.20.7", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.20.13", + "@babel/types": "^7.20.7", + "debug": "^4.1.0", + "globals": "^11.1.0" + } + }, + "@babel/types": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.7.tgz", + "integrity": "sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + } + }, + "@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, "@babel/plugin-proposal-decorators": { "version": "7.6.0", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.6.0.tgz", @@ -1950,50 +2388,290 @@ } } }, - "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.6.2.tgz", - "integrity": "sha512-NxHETdmpeSCtiatMRYWVJo7266rrvAC3DTeG5exQBIH/fMIUK7ejDNznBbn3HQl/o9peymRRg7Yqkx6PdUXmMw==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-regex": "^7.4.4", - "regexpu-core": "^4.6.0" - } - }, - "@babel/plugin-syntax-async-generators": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.2.0.tgz", - "integrity": "sha512-1ZrIRBv2t0GSlcwVoQ6VgSLpLgiN/FVQUzt9znxo7v2Ov4jJrs8RY8tv0wvDmFN3qIdMKWrmMMW6yZ0G19MfGg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-syntax-class-properties": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.10.4.tgz", - "integrity": "sha512-GCSBF7iUle6rNugfURwNmCGG3Z/2+opxAMLs1nND4bhEG5PuxTIggDBoeYYSujAlLtsupzOHYJQgPS3pivwXIA==", + "@babel/plugin-proposal-private-property-in-object": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.20.5.tgz", + "integrity": "sha512-Vq7b9dUA12ByzB4EjQTPo25sFhY+08pQDBSZRtUAkj7lb7jahaHR5igera16QZ+3my1nYR4dKsNdYj5IjPHilQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.20.5", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" }, "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", - "dev": true - } - } - }, - "@babel/plugin-syntax-decorators": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.2.0.tgz", - "integrity": "sha512-38QdqVoXdHUQfTpZo3rQwqQdWtCn5tMv4uV6r2RMfTqNBuv4ZBhz79SfaQWKTVmxHjeFv/DnXVC/+agHCklYWA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } + "@babel/code-frame": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "dev": true, + "requires": { + "@babel/highlight": "^7.18.6" + } + }, + "@babel/generator": { + "version": "7.20.14", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.14.tgz", + "integrity": "sha512-AEmuXHdcD3A52HHXxaTmYlb8q/xMEhoRP67B3T4Oq7lbmSoqroMZzjnGj3+i1io3pdnF8iBYVu4Ilj+c4hBxYg==", + "dev": true, + "requires": { + "@babel/types": "^7.20.7", + "@jridgewell/gen-mapping": "^0.3.2", + "jsesc": "^2.5.1" + } + }, + "@babel/helper-annotate-as-pure": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", + "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-create-class-features-plugin": { + "version": "7.20.12", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.20.12.tgz", + "integrity": "sha512-9OunRkbT0JQcednL0UFvbfXpAsUXiGjUk0a7sN8fUXX7Mue79cUSMjHGDRRi/Vz9vYlpIhLV5fMD5dKoMhhsNQ==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-member-expression-to-functions": "^7.20.7", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-replace-supers": "^7.20.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", + "@babel/helper-split-export-declaration": "^7.18.6" + } + }, + "@babel/helper-function-name": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", + "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", + "dev": true, + "requires": { + "@babel/template": "^7.18.10", + "@babel/types": "^7.19.0" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.20.7.tgz", + "integrity": "sha512-9J0CxJLq315fEdi4s7xK5TQaNYjZw+nDVpVqr1axNGKzdrdwYBD5b4uKv3n75aABG0rCCTK8Im8Ww7eYfMrZgw==", + "dev": true, + "requires": { + "@babel/types": "^7.20.7" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz", + "integrity": "sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + }, + "@babel/helper-replace-supers": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.20.7.tgz", + "integrity": "sha512-vujDMtB6LVfNW13jhlCrp48QNslK6JXi7lQG736HVbHz/mbf4Dc7tIRh1Xf5C0rF7BP8iiSxGMCmY6Ci1ven3A==", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-member-expression-to-functions": "^7.20.7", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.20.7", + "@babel/types": "^7.20.7" + } + }, + "@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz", + "integrity": "sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==", + "dev": true, + "requires": { + "@babel/types": "^7.20.0" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true + }, + "@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.20.15", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.15.tgz", + "integrity": "sha512-DI4a1oZuf8wC+oAJA9RW6ga3Zbe8RZFt7kD9i4qAspz3I/yHet1VvC3DiSy/fsUvv5pvJuNPh0LPOdCcqinDPg==", + "dev": true + }, + "@babel/template": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", + "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7" + } + }, + "@babel/traverse": { + "version": "7.20.13", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.13.tgz", + "integrity": "sha512-kMJXfF0T6DIS9E8cgdLCSAL+cuCK+YEZHWiLK0SXpTo8YRj5lpJu3CDNKiIBCne4m9hhTIqUg6SYTAI39tAiVQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.20.7", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.20.13", + "@babel/types": "^7.20.7", + "debug": "^4.1.0", + "globals": "^11.1.0" + } + }, + "@babel/types": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.7.tgz", + "integrity": "sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + } + }, + "@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "@babel/plugin-proposal-unicode-property-regex": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.6.2.tgz", + "integrity": "sha512-NxHETdmpeSCtiatMRYWVJo7266rrvAC3DTeG5exQBIH/fMIUK7ejDNznBbn3HQl/o9peymRRg7Yqkx6PdUXmMw==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-regex": "^7.4.4", + "regexpu-core": "^4.6.0" + } + }, + "@babel/plugin-syntax-async-generators": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.2.0.tgz", + "integrity": "sha512-1ZrIRBv2t0GSlcwVoQ6VgSLpLgiN/FVQUzt9znxo7v2Ov4jJrs8RY8tv0wvDmFN3qIdMKWrmMMW6yZ0G19MfGg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-syntax-class-properties": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.10.4.tgz", + "integrity": "sha512-GCSBF7iUle6rNugfURwNmCGG3Z/2+opxAMLs1nND4bhEG5PuxTIggDBoeYYSujAlLtsupzOHYJQgPS3pivwXIA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", + "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", + "dev": true + } + } + }, + "@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-syntax-decorators": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.2.0.tgz", + "integrity": "sha512-38QdqVoXdHUQfTpZo3rQwqQdWtCn5tMv4uV6r2RMfTqNBuv4ZBhz79SfaQWKTVmxHjeFv/DnXVC/+agHCklYWA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } }, "@babel/plugin-syntax-dynamic-import": { "version": "7.2.0", @@ -2021,6 +2699,23 @@ } } }, + "@babel/plugin-syntax-import-assertions": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz", + "integrity": "sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.19.0" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, "@babel/plugin-syntax-json-strings": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.2.0.tgz", @@ -2110,6 +2805,23 @@ } } }, + "@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, "@babel/plugin-syntax-top-level-await": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.8.3.tgz", @@ -3125,6 +3837,44 @@ "@glimmer/di": "^0.2.0" } }, + "@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true + }, + "@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", + "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + } + }, "@miragejs/pretender-node-polyfill": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/@miragejs/pretender-node-polyfill/-/pretender-node-polyfill-0.1.2.tgz", @@ -4377,6 +5127,91 @@ "resolve": "^1.4.0" } }, + "babel-plugin-polyfill-corejs2": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz", + "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.17.7", + "@babel/helper-define-polyfill-provider": "^0.3.3", + "semver": "^6.1.1" + }, + "dependencies": { + "@babel/compat-data": { + "version": "7.20.14", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.14.tgz", + "integrity": "sha512-0YpKHD6ImkWMEINCyDAD0HLLUH/lPCefG8ld9it8DJB2wnApraKuhgYTvTY1z7UFIfBTGy5LwncZ+5HWWGbhFw==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "babel-plugin-polyfill-corejs3": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz", + "integrity": "sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==", + "dev": true, + "requires": { + "@babel/helper-define-polyfill-provider": "^0.3.3", + "core-js-compat": "^3.25.1" + }, + "dependencies": { + "browserslist": { + "version": "4.21.5", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", + "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001449", + "electron-to-chromium": "^1.4.284", + "node-releases": "^2.0.8", + "update-browserslist-db": "^1.0.10" + } + }, + "caniuse-lite": { + "version": "1.0.30001450", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001450.tgz", + "integrity": "sha512-qMBmvmQmFXaSxexkjjfMvD5rnDL0+m+dUMZKoDYsGG8iZN29RuYh9eRoMvKsT6uMAWlyUUGDEQGJJYjzCIO9ew==", + "dev": true + }, + "core-js-compat": { + "version": "3.27.2", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.27.2.tgz", + "integrity": "sha512-welaYuF7ZtbYKGrIy7y3eb40d37rG1FvzEOfe7hSLd2iD6duMDqUhRfSvCGyC46HhR6Y8JXXdZ2lnRUMkPBpvg==", + "dev": true, + "requires": { + "browserslist": "^4.21.4" + } + }, + "electron-to-chromium": { + "version": "1.4.288", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.288.tgz", + "integrity": "sha512-8s9aJf3YiokIrR+HOQzNOGmEHFXVUQzXM/JaViVvKdCkNUjS+lEa/uT7xw3nDVG/IgfxiIwUGkwJ6AR1pTpYsQ==", + "dev": true + }, + "node-releases": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", + "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", + "dev": true + } + } + }, + "babel-plugin-polyfill-regenerator": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz", + "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==", + "dev": true, + "requires": { + "@babel/helper-define-polyfill-provider": "^0.3.3" + } + }, "babel-plugin-syntax-async-functions": { "version": "6.13.0", "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz", @@ -13430,160 +14265,1636 @@ "integrity": "sha512-w6GcnkxvHcNCte5FcLGEG1hUdQvlfvSN/6PtGWU/otg69Ugk8rUk51h41R0Ugoc+TNxyeFG1opRt2RlA87XzNw==", "dev": true, "requires": { - "babel-core": "^6.26.0", - "broccoli-funnel": "^2.0.1", - "broccoli-merge-trees": "^2.0.0", - "broccoli-persistent-filter": "^1.4.3", - "clone": "^2.0.0", - "hash-for-dep": "^1.2.3", - "heimdalljs-logger": "^0.1.7", - "json-stable-stringify": "^1.0.0", - "rsvp": "^4.8.2", - "workerpool": "^2.3.0" + "babel-core": "^6.26.0", + "broccoli-funnel": "^2.0.1", + "broccoli-merge-trees": "^2.0.0", + "broccoli-persistent-filter": "^1.4.3", + "clone": "^2.0.0", + "hash-for-dep": "^1.2.3", + "heimdalljs-logger": "^0.1.7", + "json-stable-stringify": "^1.0.0", + "rsvp": "^4.8.2", + "workerpool": "^2.3.0" + }, + "dependencies": { + "broccoli-merge-trees": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/broccoli-merge-trees/-/broccoli-merge-trees-2.0.1.tgz", + "integrity": "sha512-WjaexJ+I8BxP5V5RNn6um/qDRSmKoiBC/QkRi79FT9ClHfldxRyCDs9mcV7mmoaPlsshmmPaUz5jdtcKA6DClQ==", + "dev": true, + "requires": { + "broccoli-plugin": "^1.3.0", + "merge-trees": "^1.0.1" + } + } + } + }, + "broccoli-persistent-filter": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/broccoli-persistent-filter/-/broccoli-persistent-filter-1.4.6.tgz", + "integrity": "sha512-0RejLwoC95kv4kta8KAa+FmECJCK78Qgm8SRDEK7YyU0N9Cx6KpY3UCDy9WELl3mCXLN8TokNxc7/hp3lL4lfw==", + "dev": true, + "requires": { + "async-disk-cache": "^1.2.1", + "async-promise-queue": "^1.0.3", + "broccoli-plugin": "^1.0.0", + "fs-tree-diff": "^0.5.2", + "hash-for-dep": "^1.0.2", + "heimdalljs": "^0.2.1", + "heimdalljs-logger": "^0.1.7", + "mkdirp": "^0.5.1", + "promise-map-series": "^0.2.1", + "rimraf": "^2.6.1", + "rsvp": "^3.0.18", + "symlink-or-copy": "^1.0.1", + "walk-sync": "^0.3.1" + }, + "dependencies": { + "rsvp": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-3.6.2.tgz", + "integrity": "sha512-OfWGQTb9vnwRjwtA2QwpG2ICclHC3pgXZO5xt8H2EfgDquO0qVdSb5T88L4qJVAEugbS56pAuV4XZM58UX8ulw==", + "dev": true + } + } + }, + "broccoli-rollup": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/broccoli-rollup/-/broccoli-rollup-2.1.1.tgz", + "integrity": "sha512-aky/Ovg5DbsrsJEx2QCXxHLA6ZR+9u1TNVTf85soP4gL8CjGGKQ/JU8R3BZ2ntkWzo6/83RCKzX6O+nlNKR5MQ==", + "dev": true, + "requires": { + "@types/node": "^9.6.0", + "amd-name-resolver": "^1.2.0", + "broccoli-plugin": "^1.2.1", + "fs-tree-diff": "^0.5.2", + "heimdalljs": "^0.2.1", + "heimdalljs-logger": "^0.1.7", + "magic-string": "^0.24.0", + "node-modules-path": "^1.0.1", + "rollup": "^0.57.1", + "symlink-or-copy": "^1.1.8", + "walk-sync": "^0.3.1" + } + }, + "ember-cli-babel": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/ember-cli-babel/-/ember-cli-babel-6.18.0.tgz", + "integrity": "sha512-7ceC8joNYxY2wES16iIBlbPSxwKDBhYwC8drU3ZEvuPDMwVv1KzxCNu1fvxyFEBWhwaRNTUxSCsEVoTd9nosGA==", + "dev": true, + "requires": { + "amd-name-resolver": "1.2.0", + "babel-plugin-debug-macros": "^0.2.0-beta.6", + "babel-plugin-ember-modules-api-polyfill": "^2.6.0", + "babel-plugin-transform-es2015-modules-amd": "^6.24.0", + "babel-polyfill": "^6.26.0", + "babel-preset-env": "^1.7.0", + "broccoli-babel-transpiler": "^6.5.0", + "broccoli-debug": "^0.6.4", + "broccoli-funnel": "^2.0.0", + "broccoli-source": "^1.1.0", + "clone": "^2.0.0", + "ember-cli-version-checker": "^2.1.2", + "semver": "^5.5.0" + }, + "dependencies": { + "amd-name-resolver": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/amd-name-resolver/-/amd-name-resolver-1.2.0.tgz", + "integrity": "sha512-hlSTWGS1t6/xq5YCed7YALg7tKZL3rkl7UwEZ/eCIkn8JxmM6fU6Qs/1hwtjQqfuYxlffuUcgYEm0f5xP4YKaA==", + "dev": true, + "requires": { + "ensure-posix-path": "^1.0.1" + } + } + } + }, + "ember-cli-version-checker": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ember-cli-version-checker/-/ember-cli-version-checker-2.2.0.tgz", + "integrity": "sha512-G+KtYIVlSOWGcNaTFHk76xR4GdzDLzAS4uxZUKdASuFX0KJE43C6DaqL+y3VTpUFLI2FIkAS6HZ4I1YBi+S3hg==", + "dev": true, + "requires": { + "resolve": "^1.3.3", + "semver": "^5.3.0" + } + }, + "magic-string": { + "version": "0.24.1", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.24.1.tgz", + "integrity": "sha512-YBfNxbJiixMzxW40XqJEIldzHyh5f7CZKalo1uZffevyrPEX8Qgo9s0dmcORLHdV47UyvJg8/zD+6hQG3qvJrA==", + "dev": true, + "requires": { + "sourcemap-codec": "^1.4.1" + } + }, + "merge-trees": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-trees/-/merge-trees-1.0.1.tgz", + "integrity": "sha1-zL5nRWl4f53vF/1G5lJfVwC70j4=", + "dev": true, + "requires": { + "can-symlink": "^1.0.0", + "fs-tree-diff": "^0.5.4", + "heimdalljs": "^0.2.1", + "heimdalljs-logger": "^0.1.7", + "rimraf": "^2.4.3", + "symlink-or-copy": "^1.0.0" + } + }, + "rollup": { + "version": "0.57.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-0.57.1.tgz", + "integrity": "sha512-I18GBqP0qJoJC1K1osYjreqA8VAKovxuI3I81RSk0Dmr4TgloI0tAULjZaox8OsJ+n7XRrhH6i0G2By/pj1LCA==", + "dev": true, + "requires": { + "@types/acorn": "^4.0.3", + "acorn": "^5.5.3", + "acorn-dynamic-import": "^3.0.0", + "date-time": "^2.1.0", + "is-reference": "^1.1.0", + "locate-character": "^2.0.5", + "pretty-ms": "^3.1.0", + "require-relative": "^0.8.7", + "rollup-pluginutils": "^2.0.1", + "signal-exit": "^3.0.2", + "sourcemap-codec": "^1.4.1" + } + }, + "rsvp": { + "version": "4.8.5", + "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", + "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", + "dev": true + }, + "workerpool": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-2.3.3.tgz", + "integrity": "sha512-L1ovlYHp6UObYqElXXpbd214GgbEKDED0d3sj7pRdFXjNkb2+un/AUcCkceHizO0IVI6SOGGncrcjozruCkRgA==", + "dev": true, + "requires": { + "object-assign": "4.1.1" + } + } + } + }, + "ember-g-recaptcha": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ember-g-recaptcha/-/ember-g-recaptcha-1.3.0.tgz", + "integrity": "sha512-A9BLwqEEt2nIRU2/oikTatj/gIMQK1PIiGg2Yic6dnign0LSJolkqpiQpJnr7vUKjlVCI7WtvLjjJb2w56cd2g==", + "dev": true, + "requires": { + "ember-cli-babel": "^7.13.0", + "ember-cli-htmlbars": "^4.2.0" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "dev": true, + "requires": { + "@babel/highlight": "^7.18.6" + } + }, + "@babel/compat-data": { + "version": "7.20.14", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.14.tgz", + "integrity": "sha512-0YpKHD6ImkWMEINCyDAD0HLLUH/lPCefG8ld9it8DJB2wnApraKuhgYTvTY1z7UFIfBTGy5LwncZ+5HWWGbhFw==", + "dev": true + }, + "@babel/core": { + "version": "7.20.12", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.12.tgz", + "integrity": "sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg==", + "dev": true, + "requires": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.20.7", + "@babel/helper-compilation-targets": "^7.20.7", + "@babel/helper-module-transforms": "^7.20.11", + "@babel/helpers": "^7.20.7", + "@babel/parser": "^7.20.7", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.20.12", + "@babel/types": "^7.20.7", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.2", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@babel/generator": { + "version": "7.20.14", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.14.tgz", + "integrity": "sha512-AEmuXHdcD3A52HHXxaTmYlb8q/xMEhoRP67B3T4Oq7lbmSoqroMZzjnGj3+i1io3pdnF8iBYVu4Ilj+c4hBxYg==", + "dev": true, + "requires": { + "@babel/types": "^7.20.7", + "@jridgewell/gen-mapping": "^0.3.2", + "jsesc": "^2.5.1" + } + }, + "@babel/helper-annotate-as-pure": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", + "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz", + "integrity": "sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw==", + "dev": true, + "requires": { + "@babel/helper-explode-assignable-expression": "^7.18.6", + "@babel/types": "^7.18.9" + } + }, + "@babel/helper-compilation-targets": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz", + "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.20.5", + "@babel/helper-validator-option": "^7.18.6", + "browserslist": "^4.21.3", + "lru-cache": "^5.1.1", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@babel/helper-create-class-features-plugin": { + "version": "7.20.12", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.20.12.tgz", + "integrity": "sha512-9OunRkbT0JQcednL0UFvbfXpAsUXiGjUk0a7sN8fUXX7Mue79cUSMjHGDRRi/Vz9vYlpIhLV5fMD5dKoMhhsNQ==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-member-expression-to-functions": "^7.20.7", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-replace-supers": "^7.20.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", + "@babel/helper-split-export-declaration": "^7.18.6" + } + }, + "@babel/helper-create-regexp-features-plugin": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.20.5.tgz", + "integrity": "sha512-m68B1lkg3XDGX5yCvGO0kPx3v9WIYLnzjKfPcQiwntEQa5ZeRkPmo2X/ISJc8qxWGfwUr+kvZAeEzAwLec2r2w==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "regexpu-core": "^5.2.1" + } + }, + "@babel/helper-explode-assignable-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz", + "integrity": "sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-function-name": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", + "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", + "dev": true, + "requires": { + "@babel/template": "^7.18.10", + "@babel/types": "^7.19.0" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.20.7.tgz", + "integrity": "sha512-9J0CxJLq315fEdi4s7xK5TQaNYjZw+nDVpVqr1axNGKzdrdwYBD5b4uKv3n75aABG0rCCTK8Im8Ww7eYfMrZgw==", + "dev": true, + "requires": { + "@babel/types": "^7.20.7" + } + }, + "@babel/helper-module-imports": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-module-transforms": { + "version": "7.20.11", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.11.tgz", + "integrity": "sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg==", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-simple-access": "^7.20.2", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.19.1", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.20.10", + "@babel/types": "^7.20.7" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz", + "integrity": "sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + }, + "@babel/helper-remap-async-to-generator": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz", + "integrity": "sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-wrap-function": "^7.18.9", + "@babel/types": "^7.18.9" + } + }, + "@babel/helper-replace-supers": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.20.7.tgz", + "integrity": "sha512-vujDMtB6LVfNW13jhlCrp48QNslK6JXi7lQG736HVbHz/mbf4Dc7tIRh1Xf5C0rF7BP8iiSxGMCmY6Ci1ven3A==", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-member-expression-to-functions": "^7.20.7", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.20.7", + "@babel/types": "^7.20.7" + } + }, + "@babel/helper-simple-access": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", + "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", + "dev": true, + "requires": { + "@babel/types": "^7.20.2" + } + }, + "@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz", + "integrity": "sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==", + "dev": true, + "requires": { + "@babel/types": "^7.20.0" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true + }, + "@babel/helper-wrap-function": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz", + "integrity": "sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.19.0", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.20.5", + "@babel/types": "^7.20.5" + } + }, + "@babel/helpers": { + "version": "7.20.13", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.13.tgz", + "integrity": "sha512-nzJ0DWCL3gB5RCXbUO3KIMMsBY2Eqbx8mBpKGE/02PgyRQFcPQLbkQ1vyy596mZLaP+dAfD+R4ckASzNVmW3jg==", + "dev": true, + "requires": { + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.20.13", + "@babel/types": "^7.20.7" + } + }, + "@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.20.15", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.15.tgz", + "integrity": "sha512-DI4a1oZuf8wC+oAJA9RW6ga3Zbe8RZFt7kD9i4qAspz3I/yHet1VvC3DiSy/fsUvv5pvJuNPh0LPOdCcqinDPg==", + "dev": true + }, + "@babel/plugin-proposal-async-generator-functions": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz", + "integrity": "sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-remap-async-to-generator": "^7.18.9", + "@babel/plugin-syntax-async-generators": "^7.8.4" + } + }, + "@babel/plugin-proposal-class-properties": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", + "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-proposal-decorators": { + "version": "7.20.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.20.13.tgz", + "integrity": "sha512-7T6BKHa9Cpd7lCueHBBzP0nkXNina+h5giOZw+a8ZpMfPFY19VjJAjIxyFHuWkhCWgL6QMqRiY/wB1fLXzm6Mw==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.20.12", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-replace-supers": "^7.20.7", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/plugin-syntax-decorators": "^7.19.0" + } + }, + "@babel/plugin-proposal-dynamic-import": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz", + "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + } + }, + "@babel/plugin-proposal-export-namespace-from": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz", + "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + } + }, + "@babel/plugin-proposal-json-strings": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz", + "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-json-strings": "^7.8.3" + } + }, + "@babel/plugin-proposal-logical-assignment-operators": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz", + "integrity": "sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + } + }, + "@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", + "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + } + }, + "@babel/plugin-proposal-numeric-separator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", + "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + } + }, + "@babel/plugin-proposal-object-rest-spread": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz", + "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.20.5", + "@babel/helper-compilation-targets": "^7.20.7", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.20.7" + } + }, + "@babel/plugin-proposal-optional-catch-binding": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", + "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + } + }, + "@babel/plugin-proposal-optional-chaining": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.20.7.tgz", + "integrity": "sha512-T+A7b1kfjtRM51ssoOfS1+wbyCVqorfyZhT99TvxxLMirPShD8CzKMRepMlCBGM5RpHMbn8s+5MMHnPstJH6mQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + } + }, + "@babel/plugin-proposal-private-methods": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", + "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-proposal-unicode-property-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", + "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-syntax-decorators": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.19.0.tgz", + "integrity": "sha512-xaBZUEDntt4faL1yN8oIFlhfXeQAWJW7CLKYsHTUqriCUbj8xOra8bfxxKGi/UwExPFBuPdH4XfHc9rGQhrVkQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.19.0" + } + }, + "@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-typescript": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz", + "integrity": "sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.19.0" + } + }, + "@babel/plugin-transform-arrow-functions": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.20.7.tgz", + "integrity": "sha512-3poA5E7dzDomxj9WXWwuD6A5F3kc7VXwIJO+E+J8qtDtS+pXPAhrgEyh+9GBwBgPq1Z+bB+/JD60lp5jsN7JPQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.20.2" + } + }, + "@babel/plugin-transform-async-to-generator": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.20.7.tgz", + "integrity": "sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-remap-async-to-generator": "^7.18.9" + } + }, + "@babel/plugin-transform-block-scoped-functions": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz", + "integrity": "sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-block-scoping": { + "version": "7.20.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.15.tgz", + "integrity": "sha512-Vv4DMZ6MiNOhu/LdaZsT/bsLRxgL94d269Mv4R/9sp6+Mp++X/JqypZYypJXLlM4mlL352/Egzbzr98iABH1CA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.20.2" + } + }, + "@babel/plugin-transform-classes": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.20.7.tgz", + "integrity": "sha512-LWYbsiXTPKl+oBlXUGlwNlJZetXD5Am+CyBdqhPsDVjM9Jc8jwBJFrKhHf900Kfk2eZG1y9MAG3UNajol7A4VQ==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-compilation-targets": "^7.20.7", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-replace-supers": "^7.20.7", + "@babel/helper-split-export-declaration": "^7.18.6", + "globals": "^11.1.0" + } + }, + "@babel/plugin-transform-computed-properties": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.20.7.tgz", + "integrity": "sha512-Lz7MvBK6DTjElHAmfu6bfANzKcxpyNPeYBGEafyA6E5HtRpjpZwU+u7Qrgz/2OR0z+5TvKYbPdphfSaAcZBrYQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/template": "^7.20.7" + } + }, + "@babel/plugin-transform-destructuring": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.20.7.tgz", + "integrity": "sha512-Xwg403sRrZb81IVB79ZPqNQME23yhugYVqgTxAhT99h485F4f+GMELFhhOsscDUB7HCswepKeCKLn/GZvUKoBA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.20.2" + } + }, + "@babel/plugin-transform-dotall-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz", + "integrity": "sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-duplicate-keys": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz", + "integrity": "sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-exponentiation-operator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz", + "integrity": "sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==", + "dev": true, + "requires": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-for-of": { + "version": "7.18.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz", + "integrity": "sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-function-name": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz", + "integrity": "sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==", + "dev": true, + "requires": { + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-literals": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz", + "integrity": "sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-member-expression-literals": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz", + "integrity": "sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-modules-amd": { + "version": "7.20.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.20.11.tgz", + "integrity": "sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.20.11", + "@babel/helper-plugin-utils": "^7.20.2" + } + }, + "@babel/plugin-transform-modules-commonjs": { + "version": "7.20.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.20.11.tgz", + "integrity": "sha512-S8e1f7WQ7cimJQ51JkAaDrEtohVEitXjgCGAS2N8S31Y42E+kWwfSz83LYz57QdBm7q9diARVqanIaH2oVgQnw==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.20.11", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-simple-access": "^7.20.2" + } + }, + "@babel/plugin-transform-modules-systemjs": { + "version": "7.20.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.20.11.tgz", + "integrity": "sha512-vVu5g9BPQKSFEmvt2TA4Da5N+QVS66EX21d8uoOihC+OCpUoGvzVsXeqFdtAEfVa5BILAeFt+U7yVmLbQnAJmw==", + "dev": true, + "requires": { + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-module-transforms": "^7.20.11", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-validator-identifier": "^7.19.1" + } + }, + "@babel/plugin-transform-modules-umd": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz", + "integrity": "sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.20.5.tgz", + "integrity": "sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.20.5", + "@babel/helper-plugin-utils": "^7.20.2" + } + }, + "@babel/plugin-transform-new-target": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz", + "integrity": "sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-object-super": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz", + "integrity": "sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-replace-supers": "^7.18.6" + } + }, + "@babel/plugin-transform-parameters": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.20.7.tgz", + "integrity": "sha512-WiWBIkeHKVOSYPO0pWkxGPfKeWrCJyD3NJ53+Lrp/QMSZbsVPovrVl2aWZ19D/LTVnaDv5Ap7GJ/B2CTOZdrfA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.20.2" + } + }, + "@babel/plugin-transform-property-literals": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz", + "integrity": "sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-regenerator": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.20.5.tgz", + "integrity": "sha512-kW/oO7HPBtntbsahzQ0qSE3tFvkFwnbozz3NWFhLGqH75vLEg+sCGngLlhVkePlCs3Jv0dBBHDzCHxNiFAQKCQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.20.2", + "regenerator-transform": "^0.15.1" + } + }, + "@babel/plugin-transform-reserved-words": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz", + "integrity": "sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-runtime": { + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.19.6.tgz", + "integrity": "sha512-PRH37lz4JU156lYFW1p8OxE5i7d6Sl/zV58ooyr+q1J1lnQPyg5tIiXlIwNVhJaY4W3TmOtdc8jqdXQcB1v5Yw==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.19.0", + "babel-plugin-polyfill-corejs2": "^0.3.3", + "babel-plugin-polyfill-corejs3": "^0.6.0", + "babel-plugin-polyfill-regenerator": "^0.4.1", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@babel/plugin-transform-shorthand-properties": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz", + "integrity": "sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-spread": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz", + "integrity": "sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0" + } + }, + "@babel/plugin-transform-sticky-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz", + "integrity": "sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-template-literals": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz", + "integrity": "sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-typeof-symbol": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz", + "integrity": "sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-typescript": { + "version": "7.20.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.20.13.tgz", + "integrity": "sha512-O7I/THxarGcDZxkgWKMUrk7NK1/WbHAg3Xx86gqS6x9MTrNL6AwIluuZ96ms4xeDe6AVx6rjHbWHP7x26EPQBA==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.20.12", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-typescript": "^7.20.0" + } + }, + "@babel/plugin-transform-unicode-escapes": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz", + "integrity": "sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-unicode-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz", + "integrity": "sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/polyfill": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/polyfill/-/polyfill-7.12.1.tgz", + "integrity": "sha512-X0pi0V6gxLi6lFZpGmeNa4zxtwEmCs42isWLNjZZDE0Y8yVfgu0T2OAHlzBbdYlqbW/YXVvoBHpATEM+goCj8g==", + "dev": true, + "requires": { + "core-js": "^2.6.5", + "regenerator-runtime": "^0.13.4" + } + }, + "@babel/preset-env": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.20.2.tgz", + "integrity": "sha512-1G0efQEWR1EHkKvKHqbG+IN/QdgwfByUpM5V5QroDzGV2t3S/WXNQd693cHiHTlCFMpr9B6FkPFXDA2lQcKoDg==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.20.1", + "@babel/helper-compilation-targets": "^7.20.0", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-validator-option": "^7.18.6", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-async-generator-functions": "^7.20.1", + "@babel/plugin-proposal-class-properties": "^7.18.6", + "@babel/plugin-proposal-class-static-block": "^7.18.6", + "@babel/plugin-proposal-dynamic-import": "^7.18.6", + "@babel/plugin-proposal-export-namespace-from": "^7.18.9", + "@babel/plugin-proposal-json-strings": "^7.18.6", + "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", + "@babel/plugin-proposal-numeric-separator": "^7.18.6", + "@babel/plugin-proposal-object-rest-spread": "^7.20.2", + "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", + "@babel/plugin-proposal-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-private-methods": "^7.18.6", + "@babel/plugin-proposal-private-property-in-object": "^7.18.6", + "@babel/plugin-proposal-unicode-property-regex": "^7.18.6", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-import-assertions": "^7.20.0", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-transform-arrow-functions": "^7.18.6", + "@babel/plugin-transform-async-to-generator": "^7.18.6", + "@babel/plugin-transform-block-scoped-functions": "^7.18.6", + "@babel/plugin-transform-block-scoping": "^7.20.2", + "@babel/plugin-transform-classes": "^7.20.2", + "@babel/plugin-transform-computed-properties": "^7.18.9", + "@babel/plugin-transform-destructuring": "^7.20.2", + "@babel/plugin-transform-dotall-regex": "^7.18.6", + "@babel/plugin-transform-duplicate-keys": "^7.18.9", + "@babel/plugin-transform-exponentiation-operator": "^7.18.6", + "@babel/plugin-transform-for-of": "^7.18.8", + "@babel/plugin-transform-function-name": "^7.18.9", + "@babel/plugin-transform-literals": "^7.18.9", + "@babel/plugin-transform-member-expression-literals": "^7.18.6", + "@babel/plugin-transform-modules-amd": "^7.19.6", + "@babel/plugin-transform-modules-commonjs": "^7.19.6", + "@babel/plugin-transform-modules-systemjs": "^7.19.6", + "@babel/plugin-transform-modules-umd": "^7.18.6", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.19.1", + "@babel/plugin-transform-new-target": "^7.18.6", + "@babel/plugin-transform-object-super": "^7.18.6", + "@babel/plugin-transform-parameters": "^7.20.1", + "@babel/plugin-transform-property-literals": "^7.18.6", + "@babel/plugin-transform-regenerator": "^7.18.6", + "@babel/plugin-transform-reserved-words": "^7.18.6", + "@babel/plugin-transform-shorthand-properties": "^7.18.6", + "@babel/plugin-transform-spread": "^7.19.0", + "@babel/plugin-transform-sticky-regex": "^7.18.6", + "@babel/plugin-transform-template-literals": "^7.18.9", + "@babel/plugin-transform-typeof-symbol": "^7.18.9", + "@babel/plugin-transform-unicode-escapes": "^7.18.10", + "@babel/plugin-transform-unicode-regex": "^7.18.6", + "@babel/preset-modules": "^0.1.5", + "@babel/types": "^7.20.2", + "babel-plugin-polyfill-corejs2": "^0.3.3", + "babel-plugin-polyfill-corejs3": "^0.6.0", + "babel-plugin-polyfill-regenerator": "^0.4.1", + "core-js-compat": "^3.25.1", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@babel/preset-modules": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", + "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + } + }, + "@babel/runtime": { + "version": "7.12.18", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.18.tgz", + "integrity": "sha512-BogPQ7ciE6SYAUPtlm9tWbgI9+2AgqSam6QivMgXgAT+fKbgppaj4ZX15MHeLC1PVF5sNk70huBu20XxWOs8Cg==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "@babel/template": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", + "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7" + } + }, + "@babel/traverse": { + "version": "7.20.13", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.13.tgz", + "integrity": "sha512-kMJXfF0T6DIS9E8cgdLCSAL+cuCK+YEZHWiLK0SXpTo8YRj5lpJu3CDNKiIBCne4m9hhTIqUg6SYTAI39tAiVQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.20.7", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.20.13", + "@babel/types": "^7.20.7", + "debug": "^4.1.0", + "globals": "^11.1.0" + } + }, + "@babel/types": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.7.tgz", + "integrity": "sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + } + }, + "@ember/edition-utils": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ember/edition-utils/-/edition-utils-1.2.0.tgz", + "integrity": "sha512-VmVq/8saCaPdesQmftPqbFtxJWrzxNGSQ+e8x8LLe3Hjm36pJ04Q8LeORGZkAeOhldoUX9seLGmSaHeXkIqoog==", + "dev": true + }, + "@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "babel-plugin-debug-macros": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/babel-plugin-debug-macros/-/babel-plugin-debug-macros-0.3.4.tgz", + "integrity": "sha512-wfel/vb3pXfwIDZUrkoDrn5FHmlWI96PCJ3UCDv2a86poJ3EQrnArNW5KfHSVJ9IOgxHbo748cQt7sDU+0KCEw==", + "dev": true, + "requires": { + "semver": "^5.3.0" + } + }, + "babel-plugin-ember-modules-api-polyfill": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/babel-plugin-ember-modules-api-polyfill/-/babel-plugin-ember-modules-api-polyfill-3.5.0.tgz", + "integrity": "sha512-pJajN/DkQUnStw0Az8c6khVcMQHgzqWr61lLNtVeu0g61LRW0k9jyK7vaedrHDWGe/Qe8sxG5wpiyW9NsMqFzA==", + "dev": true, + "requires": { + "ember-rfc176-data": "^0.3.17" + } + }, + "babel-plugin-htmlbars-inline-precompile": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/babel-plugin-htmlbars-inline-precompile/-/babel-plugin-htmlbars-inline-precompile-3.2.0.tgz", + "integrity": "sha512-IUeZmgs9tMUGXYu1vfke5I18yYJFldFGdNFQOWslXTnDWXzpwPih7QFduUqvT+awDpDuNtXpdt5JAf43Q1Hhzg==", + "dev": true + }, + "broccoli-babel-transpiler": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/broccoli-babel-transpiler/-/broccoli-babel-transpiler-7.8.1.tgz", + "integrity": "sha512-6IXBgfRt7HZ61g67ssBc6lBb3Smw3DPZ9dEYirgtvXWpRZ2A9M22nxy6opEwJDgDJzlu/bB7ToppW33OFkA1gA==", + "dev": true, + "requires": { + "@babel/core": "^7.12.0", + "@babel/polyfill": "^7.11.5", + "broccoli-funnel": "^2.0.2", + "broccoli-merge-trees": "^3.0.2", + "broccoli-persistent-filter": "^2.2.1", + "clone": "^2.1.2", + "hash-for-dep": "^1.4.7", + "heimdalljs": "^0.2.1", + "heimdalljs-logger": "^0.1.9", + "json-stable-stringify": "^1.0.1", + "rsvp": "^4.8.4", + "workerpool": "^3.1.1" + } + }, + "broccoli-plugin": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/broccoli-plugin/-/broccoli-plugin-3.1.0.tgz", + "integrity": "sha512-7w7FP8WJYjLvb0eaw27LO678TGGaom++49O1VYIuzjhXjK5kn2+AMlDm7CaUFw4F7CLGoVQeZ84d8gICMJa4lA==", + "dev": true, + "requires": { + "broccoli-node-api": "^1.6.0", + "broccoli-output-wrapper": "^2.0.0", + "fs-merger": "^3.0.1", + "promise-map-series": "^0.2.1", + "quick-temp": "^0.1.3", + "rimraf": "^2.3.4", + "symlink-or-copy": "^1.1.8" + }, + "dependencies": { + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "broccoli-source": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/broccoli-source/-/broccoli-source-2.1.2.tgz", + "integrity": "sha512-1lLayO4wfS0c0Sj50VfHJXNWf94FYY0WUhxj0R77thbs6uWI7USiOWFqQV5dRmhAJnoKaGN4WyLGQbgjgiYFwQ==", + "dev": true + }, + "browserslist": { + "version": "4.21.5", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", + "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001449", + "electron-to-chromium": "^1.4.284", + "node-releases": "^2.0.8", + "update-browserslist-db": "^1.0.10" + } + }, + "caniuse-lite": { + "version": "1.0.30001450", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001450.tgz", + "integrity": "sha512-qMBmvmQmFXaSxexkjjfMvD5rnDL0+m+dUMZKoDYsGG8iZN29RuYh9eRoMvKsT6uMAWlyUUGDEQGJJYjzCIO9ew==", + "dev": true + }, + "convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true + }, + "core-js-compat": { + "version": "3.27.2", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.27.2.tgz", + "integrity": "sha512-welaYuF7ZtbYKGrIy7y3eb40d37rG1FvzEOfe7hSLd2iD6duMDqUhRfSvCGyC46HhR6Y8JXXdZ2lnRUMkPBpvg==", + "dev": true, + "requires": { + "browserslist": "^4.21.4" + } + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "electron-to-chromium": { + "version": "1.4.288", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.288.tgz", + "integrity": "sha512-8s9aJf3YiokIrR+HOQzNOGmEHFXVUQzXM/JaViVvKdCkNUjS+lEa/uT7xw3nDVG/IgfxiIwUGkwJ6AR1pTpYsQ==", + "dev": true + }, + "ember-cli-babel": { + "version": "7.26.11", + "resolved": "https://registry.npmjs.org/ember-cli-babel/-/ember-cli-babel-7.26.11.tgz", + "integrity": "sha512-JJYeYjiz/JTn34q7F5DSOjkkZqy8qwFOOxXfE6pe9yEJqWGu4qErKxlz8I22JoVEQ/aBUO+OcKTpmctvykM9YA==", + "dev": true, + "requires": { + "@babel/core": "^7.12.0", + "@babel/helper-compilation-targets": "^7.12.0", + "@babel/plugin-proposal-class-properties": "^7.16.5", + "@babel/plugin-proposal-decorators": "^7.13.5", + "@babel/plugin-proposal-private-methods": "^7.16.5", + "@babel/plugin-proposal-private-property-in-object": "^7.16.5", + "@babel/plugin-transform-modules-amd": "^7.13.0", + "@babel/plugin-transform-runtime": "^7.13.9", + "@babel/plugin-transform-typescript": "^7.13.0", + "@babel/polyfill": "^7.11.5", + "@babel/preset-env": "^7.16.5", + "@babel/runtime": "7.12.18", + "amd-name-resolver": "^1.3.1", + "babel-plugin-debug-macros": "^0.3.4", + "babel-plugin-ember-data-packages-polyfill": "^0.1.2", + "babel-plugin-ember-modules-api-polyfill": "^3.5.0", + "babel-plugin-module-resolver": "^3.2.0", + "broccoli-babel-transpiler": "^7.8.0", + "broccoli-debug": "^0.6.4", + "broccoli-funnel": "^2.0.2", + "broccoli-source": "^2.1.2", + "calculate-cache-key-for-tree": "^2.0.0", + "clone": "^2.1.2", + "ember-cli-babel-plugin-helpers": "^1.1.1", + "ember-cli-version-checker": "^4.1.0", + "ensure-posix-path": "^1.0.2", + "fixturify-project": "^1.10.0", + "resolve-package-path": "^3.1.0", + "rimraf": "^3.0.1", + "semver": "^5.5.0" + } + }, + "ember-cli-babel-plugin-helpers": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ember-cli-babel-plugin-helpers/-/ember-cli-babel-plugin-helpers-1.1.1.tgz", + "integrity": "sha512-sKvOiPNHr5F/60NLd7SFzMpYPte/nnGkq/tMIfXejfKHIhaiIkYFqX8Z9UFTKWLLn+V7NOaby6niNPZUdvKCRw==", + "dev": true + }, + "ember-cli-htmlbars": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/ember-cli-htmlbars/-/ember-cli-htmlbars-4.5.0.tgz", + "integrity": "sha512-bYJpK1pqFu9AadDAGTw05g2LMNzY8xTCIqQm7dMJmKEoUpLRFbPf4SfHXrktzDh7Q5iggl6Skzf1M0bPlIxARw==", + "dev": true, + "requires": { + "@ember/edition-utils": "^1.2.0", + "babel-plugin-htmlbars-inline-precompile": "^3.2.0", + "broccoli-debug": "^0.6.5", + "broccoli-persistent-filter": "^2.3.1", + "broccoli-plugin": "^3.1.0", + "common-tags": "^1.8.0", + "ember-cli-babel-plugin-helpers": "^1.1.0", + "fs-tree-diff": "^2.0.1", + "hash-for-dep": "^1.5.1", + "heimdalljs-logger": "^0.1.10", + "json-stable-stringify": "^1.0.1", + "semver": "^6.3.0", + "strip-bom": "^4.0.0", + "walk-sync": "^2.0.2" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "ember-cli-version-checker": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ember-cli-version-checker/-/ember-cli-version-checker-4.1.1.tgz", + "integrity": "sha512-bzEWsTMXUGEJfxcAGWPe6kI7oHEGD3jaxUWDYPTqzqGhNkgPwXTBgoWs9zG1RaSMaOPFnloWuxRcoHi4TrYS3Q==", + "dev": true, + "requires": { + "resolve-package-path": "^2.0.0", + "semver": "^6.3.0", + "silent-error": "^1.1.1" }, "dependencies": { - "broccoli-merge-trees": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/broccoli-merge-trees/-/broccoli-merge-trees-2.0.1.tgz", - "integrity": "sha512-WjaexJ+I8BxP5V5RNn6um/qDRSmKoiBC/QkRi79FT9ClHfldxRyCDs9mcV7mmoaPlsshmmPaUz5jdtcKA6DClQ==", + "resolve-package-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-package-path/-/resolve-package-path-2.0.0.tgz", + "integrity": "sha512-/CLuzodHO2wyyHTzls5Qr+EFeG6RcW4u6//gjYvUfcfyuplIX1SSccU+A5A9A78Gmezkl3NBkFAMxLbzTY9TJA==", "dev": true, "requires": { - "broccoli-plugin": "^1.3.0", - "merge-trees": "^1.0.1" + "path-root": "^0.1.1", + "resolve": "^1.13.1" } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true } } }, - "broccoli-persistent-filter": { - "version": "1.4.6", - "resolved": "https://registry.npmjs.org/broccoli-persistent-filter/-/broccoli-persistent-filter-1.4.6.tgz", - "integrity": "sha512-0RejLwoC95kv4kta8KAa+FmECJCK78Qgm8SRDEK7YyU0N9Cx6KpY3UCDy9WELl3mCXLN8TokNxc7/hp3lL4lfw==", + "ember-rfc176-data": { + "version": "0.3.18", + "resolved": "https://registry.npmjs.org/ember-rfc176-data/-/ember-rfc176-data-0.3.18.tgz", + "integrity": "sha512-JtuLoYGSjay1W3MQAxt3eINWXNYYQliK90tLwtb8aeCuQK8zKGCRbBodVIrkcTqshULMnRuTOS6t1P7oQk3g6Q==", + "dev": true + }, + "fs-tree-diff": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fs-tree-diff/-/fs-tree-diff-2.0.1.tgz", + "integrity": "sha512-x+CfAZ/lJHQqwlD64pYM5QxWjzWhSjroaVsr8PW831zOApL55qPibed0c+xebaLWVr2BnHFoHdrwOv8pzt8R5A==", "dev": true, "requires": { - "async-disk-cache": "^1.2.1", - "async-promise-queue": "^1.0.3", - "broccoli-plugin": "^1.0.0", - "fs-tree-diff": "^0.5.2", - "hash-for-dep": "^1.0.2", - "heimdalljs": "^0.2.1", + "@types/symlink-or-copy": "^1.2.0", "heimdalljs-logger": "^0.1.7", - "mkdirp": "^0.5.1", - "promise-map-series": "^0.2.1", - "rimraf": "^2.6.1", - "rsvp": "^3.0.18", - "symlink-or-copy": "^1.0.1", - "walk-sync": "^0.3.1" - }, - "dependencies": { - "rsvp": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-3.6.2.tgz", - "integrity": "sha512-OfWGQTb9vnwRjwtA2QwpG2ICclHC3pgXZO5xt8H2EfgDquO0qVdSb5T88L4qJVAEugbS56pAuV4XZM58UX8ulw==", - "dev": true - } + "object-assign": "^4.1.0", + "path-posix": "^1.0.0", + "symlink-or-copy": "^1.1.8" } }, - "broccoli-rollup": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/broccoli-rollup/-/broccoli-rollup-2.1.1.tgz", - "integrity": "sha512-aky/Ovg5DbsrsJEx2QCXxHLA6ZR+9u1TNVTf85soP4gL8CjGGKQ/JU8R3BZ2ntkWzo6/83RCKzX6O+nlNKR5MQ==", + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true + }, + "json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true + }, + "matcher-collection": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/matcher-collection/-/matcher-collection-2.0.1.tgz", + "integrity": "sha512-daE62nS2ZQsDg9raM0IlZzLmI2u+7ZapXBwdoeBUKAYERPDDIc0qNqA8E0Rp2D+gspKR7BgIFP52GeujaGXWeQ==", "dev": true, "requires": { - "@types/node": "^9.6.0", - "amd-name-resolver": "^1.2.0", - "broccoli-plugin": "^1.2.1", - "fs-tree-diff": "^0.5.2", - "heimdalljs": "^0.2.1", - "heimdalljs-logger": "^0.1.7", - "magic-string": "^0.24.0", - "node-modules-path": "^1.0.1", - "rollup": "^0.57.1", - "symlink-or-copy": "^1.1.8", - "walk-sync": "^0.3.1" + "@types/minimatch": "^3.0.3", + "minimatch": "^3.0.2" } }, - "ember-cli-babel": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/ember-cli-babel/-/ember-cli-babel-6.18.0.tgz", - "integrity": "sha512-7ceC8joNYxY2wES16iIBlbPSxwKDBhYwC8drU3ZEvuPDMwVv1KzxCNu1fvxyFEBWhwaRNTUxSCsEVoTd9nosGA==", + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node-releases": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", + "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", + "dev": true + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true + }, + "regenerate-unicode-properties": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz", + "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==", "dev": true, "requires": { - "amd-name-resolver": "1.2.0", - "babel-plugin-debug-macros": "^0.2.0-beta.6", - "babel-plugin-ember-modules-api-polyfill": "^2.6.0", - "babel-plugin-transform-es2015-modules-amd": "^6.24.0", - "babel-polyfill": "^6.26.0", - "babel-preset-env": "^1.7.0", - "broccoli-babel-transpiler": "^6.5.0", - "broccoli-debug": "^0.6.4", - "broccoli-funnel": "^2.0.0", - "broccoli-source": "^1.1.0", - "clone": "^2.0.0", - "ember-cli-version-checker": "^2.1.2", - "semver": "^5.5.0" - }, - "dependencies": { - "amd-name-resolver": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/amd-name-resolver/-/amd-name-resolver-1.2.0.tgz", - "integrity": "sha512-hlSTWGS1t6/xq5YCed7YALg7tKZL3rkl7UwEZ/eCIkn8JxmM6fU6Qs/1hwtjQqfuYxlffuUcgYEm0f5xP4YKaA==", - "dev": true, - "requires": { - "ensure-posix-path": "^1.0.1" - } - } + "regenerate": "^1.4.2" } }, - "ember-cli-version-checker": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ember-cli-version-checker/-/ember-cli-version-checker-2.2.0.tgz", - "integrity": "sha512-G+KtYIVlSOWGcNaTFHk76xR4GdzDLzAS4uxZUKdASuFX0KJE43C6DaqL+y3VTpUFLI2FIkAS6HZ4I1YBi+S3hg==", + "regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "dev": true + }, + "regenerator-transform": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz", + "integrity": "sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==", "dev": true, "requires": { - "resolve": "^1.3.3", - "semver": "^5.3.0" + "@babel/runtime": "^7.8.4" } }, - "magic-string": { - "version": "0.24.1", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.24.1.tgz", - "integrity": "sha512-YBfNxbJiixMzxW40XqJEIldzHyh5f7CZKalo1uZffevyrPEX8Qgo9s0dmcORLHdV47UyvJg8/zD+6hQG3qvJrA==", + "regexpu-core": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.2.2.tgz", + "integrity": "sha512-T0+1Zp2wjF/juXMrMxHxidqGYn8U4R+zleSJhX9tQ1PUsS8a9UtYfbsF9LdiVgNX3kiX8RNaKM42nfSgvFJjmw==", "dev": true, "requires": { - "sourcemap-codec": "^1.4.1" + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.1.0", + "regjsgen": "^0.7.1", + "regjsparser": "^0.9.1", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" } }, - "merge-trees": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-trees/-/merge-trees-1.0.1.tgz", - "integrity": "sha1-zL5nRWl4f53vF/1G5lJfVwC70j4=", + "regjsgen": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.7.1.tgz", + "integrity": "sha512-RAt+8H2ZEzHeYWxZ3H2z6tF18zyyOnlcdaafLrm21Bguj7uZy6ULibiAFdXEtKQY4Sy7wDTwDiOazasMLc4KPA==", + "dev": true + }, + "regjsparser": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", + "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", "dev": true, "requires": { - "can-symlink": "^1.0.0", - "fs-tree-diff": "^0.5.4", - "heimdalljs": "^0.2.1", - "heimdalljs-logger": "^0.1.7", - "rimraf": "^2.4.3", - "symlink-or-copy": "^1.0.0" + "jsesc": "~0.5.0" + }, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "dev": true + } } }, - "rollup": { - "version": "0.57.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-0.57.1.tgz", - "integrity": "sha512-I18GBqP0qJoJC1K1osYjreqA8VAKovxuI3I81RSk0Dmr4TgloI0tAULjZaox8OsJ+n7XRrhH6i0G2By/pj1LCA==", + "resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", "dev": true, "requires": { - "@types/acorn": "^4.0.3", - "acorn": "^5.5.3", - "acorn-dynamic-import": "^3.0.0", - "date-time": "^2.1.0", - "is-reference": "^1.1.0", - "locate-character": "^2.0.5", - "pretty-ms": "^3.1.0", - "require-relative": "^0.8.7", - "rollup-pluginutils": "^2.0.1", - "signal-exit": "^3.0.2", - "sourcemap-codec": "^1.4.1" + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "resolve-package-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/resolve-package-path/-/resolve-package-path-3.1.0.tgz", + "integrity": "sha512-2oC2EjWbMJwvSN6Z7DbDfJMnD8MYEouaLn5eIX0j8XwPsYCVIyY9bbnX88YHVkbr8XHqvZrYbxaLPibfTYKZMA==", + "dev": true, + "requires": { + "path-root": "^0.1.1", + "resolve": "^1.17.0" + } + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" } }, "rsvp": { @@ -13592,13 +15903,50 @@ "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", "dev": true }, - "workerpool": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-2.3.3.tgz", - "integrity": "sha512-L1ovlYHp6UObYqElXXpbd214GgbEKDED0d3sj7pRdFXjNkb2+un/AUcCkceHizO0IVI6SOGGncrcjozruCkRgA==", + "strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true + }, + "unicode-canonical-property-names-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", + "dev": true + }, + "unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", "dev": true, "requires": { - "object-assign": "4.1.1" + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + } + }, + "unicode-match-property-value-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", + "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", + "dev": true + }, + "unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "dev": true + }, + "walk-sync": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/walk-sync/-/walk-sync-2.2.0.tgz", + "integrity": "sha512-IC8sL7aB4/ZgFcGI2T1LczZeFWZ06b3zoHH7jBPyHxOtIIz1jppWHjjEXkOFvFojBVAK9pV7g47xOZ4LW3QLfg==", + "dev": true, + "requires": { + "@types/minimatch": "^3.0.3", + "ensure-posix-path": "^1.1.0", + "matcher-collection": "^2.0.0", + "minimatch": "^3.0.4" } } } @@ -21674,6 +24022,15 @@ "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", "dev": true }, + "is-core-module": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, "is-data-descriptor": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", @@ -24130,6 +26487,12 @@ "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", "dev": true }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, "picomatch": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.0.7.tgz", @@ -26463,6 +28826,12 @@ "has-flag": "^3.0.0" } }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true + }, "svgo": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.0.tgz", @@ -27262,6 +29631,24 @@ "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", "dev": true }, + "update-browserslist-db": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", + "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "dev": true, + "requires": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "dependencies": { + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + } + } + }, "uri-js": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", diff --git a/package.json b/package.json index d41efd96a8..af22a4ff08 100644 --- a/package.json +++ b/package.json @@ -63,6 +63,7 @@ "ember-export-application-global": "^2.0.0", "ember-feature-flags": "^5.0.0", "ember-fetch": "^6.0.0", + "ember-g-recaptcha": "^1.3.0", "ember-in-viewport": "^3.0.0", "ember-inflector": "^3.0.1", "ember-intercom-io": "^1.2.0", From aa51d29213197f71ace84312a54104e949ed5d38 Mon Sep 17 00:00:00 2001 From: AndriiMysko Date: Thu, 9 Feb 2023 12:06:17 +0200 Subject: [PATCH 02/43] Fix tests --- app/components/billing/payment-details-tab.js | 15 +++++++++------ app/components/billing/summary.js | 13 +++++++++++++ app/components/profile-nav.js | 6 +++--- app/router.js | 1 + app/routes/organization/payment_details.js | 11 +++++++++++ app/templates/components/billing/summary.hbs | 5 +++++ app/templates/components/profile-nav.hbs | 15 +++++++++++---- app/templates/organization/payment_details.hbs | 5 +++++ tests/acceptance/profile/billing-test.js | 15 --------------- .../components/billing/invoices-test.js | 4 ++-- 10 files changed, 60 insertions(+), 30 deletions(-) create mode 100644 app/routes/organization/payment_details.js create mode 100644 app/templates/organization/payment_details.hbs diff --git a/app/components/billing/payment-details-tab.js b/app/components/billing/payment-details-tab.js index 538d872338..df5dff3dc0 100644 --- a/app/components/billing/payment-details-tab.js +++ b/app/components/billing/payment-details-tab.js @@ -32,14 +32,17 @@ export default Component.extend({ showSwitchToFreeModal: false, showPlanSwitchWarning: false, - subscription: reads('account.subscription'), + v1subscription: reads('account.subscription'), v2subscription: reads('account.v2subscription'), isV2SubscriptionEmpty: empty('v2subscription'), - isSubscriptionEmpty: empty('subscription'), + isSubscriptionEmpty: empty('v1subscription'), isSubscriptionsEmpty: and('isSubscriptionEmpty', 'isV2SubscriptionEmpty'), hasV2Subscription: not('isV2SubscriptionEmpty'), - invoices: computed('subscription.id', 'v2subscription.id', function () { - const subscriptionId = this.isV2SubscriptionEmpty ? this.get('subscription.id') : this.get('v2subscription.id'); + subscription: computed('v1subscription', 'v2subscription', function () { + return this.isV2SubscriptionEmpty ? this.get('v1subscription') : this.get('v2subscription'); + }), + invoices: computed('v1subscription.id', 'v2subscription.id', function () { + const subscriptionId = this.isV2SubscriptionEmpty ? this.get('v1subscription.id') : this.get('v2subscription.id'); const type = this.isV2SubscriptionEmpty ? 1 : 2; if (subscriptionId) { return this.store.query('invoice', { type, subscriptionId }); @@ -54,7 +57,7 @@ export default Component.extend({ return paymentChangesBlockCaptcha || paymentChangesBlockCredit; }), - billingInfo: reads('v2subscription.billingInfo'), + billingInfo: reads('subscription.billingInfo'), country: reads('billingInfo.country'), firstName: reads('billingInfo.firstName'), @@ -71,7 +74,7 @@ export default Component.extend({ category: 'Subscription', }); const { stripeElement } = this; - const subscription = this.v2subscription; + const subscription = this.subscription; try { let token = null; if (stripeElement) { diff --git a/app/components/billing/summary.js b/app/components/billing/summary.js index 045682469f..cace58387c 100644 --- a/app/components/billing/summary.js +++ b/app/components/billing/summary.js @@ -1,7 +1,10 @@ import Component from '@ember/component'; +import { computed } from '@ember/object'; import { reads, or, not, and, bool } from '@ember/object/computed'; +import { inject as service } from '@ember/service'; export default Component.extend({ + store: service(), subscription: null, account: null, @@ -25,4 +28,14 @@ export default Component.extend({ isGithubSubscription: reads('subscription.isGithub'), expiredStripeSubscription: reads('account.expiredStripeSubscription'), hasExpiredStripeSubscription: bool('expiredStripeSubscription'), + + invoices: computed('subscription.id', function () { + const subscriptionId = this.get('subscription.id'); + const type = 1; + if (subscriptionId) { + return this.store.query('invoice', { type, subscriptionId }); + } else { + return []; + } + }), }); diff --git a/app/components/profile-nav.js b/app/components/profile-nav.js index 3302218525..223a37d29f 100644 --- a/app/components/profile-nav.js +++ b/app/components/profile-nav.js @@ -68,11 +68,11 @@ export default Component.extend({ const isEnterprise = this.features.get('enterpriseVersion'); return !isEnterprise && !isAssemblaUser && !!billingEndpoint; }), - showPaymentDetailsTab: computed('showSubscriptionTab', 'isOrganization', 'isOrganizationAdmin', function () { + showPaymentDetailsTab: computed('showSubscriptionTab', 'isOrganization', 'isOrganizationAdmin', 'model.hasV2Subscription', function () { if (this.isOrganization) { - return this.showSubscriptionTab && this.isOrganizationAdmin; + return this.showSubscriptionTab && this.model.hasV2Subscription && this.isOrganizationAdmin; } else { - return this.showSubscriptionTab; + return this.showSubscriptionTab && this.model.hasV2Subscription; } }), showPlanUsageTab: and('showSubscriptionTab', 'model.hasCredits'), diff --git a/app/router.js b/app/router.js index 46441041e2..413a8f9132 100644 --- a/app/router.js +++ b/app/router.js @@ -49,6 +49,7 @@ Router.map(function () { this.route('settings', { path: '/preferences' }); this.route('billing', { path: '/plan' }); this.route('plan_usage', { path: '/plan/usage' }); + this.route('payment_details', { path: '/payment-details' }); this.route('migrate'); }); this.route('unsubscribe', { path: '/account/preferences/unsubscribe' }); diff --git a/app/routes/organization/payment_details.js b/app/routes/organization/payment_details.js new file mode 100644 index 0000000000..09c924279a --- /dev/null +++ b/app/routes/organization/payment_details.js @@ -0,0 +1,11 @@ +import TravisRoute from 'travis/routes/basic'; +import AccountPaymentDetailsMixin from 'travis/mixins/route/account/payment_details'; +import { hash } from 'rsvp'; + +export default TravisRoute.extend(AccountPaymentDetailsMixin, { + model() { + return hash({ + account: this.modelFor('organization'), + }); + } +}); diff --git a/app/templates/components/billing/summary.hbs b/app/templates/components/billing/summary.hbs index 48eef81599..d678562d45 100644 --- a/app/templates/components/billing/summary.hbs +++ b/app/templates/components/billing/summary.hbs @@ -89,5 +89,10 @@ {{#if (and this.subscription.isStripe (not this.showPlansSelector))}} +
    {{/if}} diff --git a/app/templates/components/profile-nav.hbs b/app/templates/components/profile-nav.hbs index 2b78537a7f..d9166cc33f 100644 --- a/app/templates/components/profile-nav.hbs +++ b/app/templates/components/profile-nav.hbs @@ -154,6 +154,13 @@ {{/if}} + {{#if this.showMigrateTab}} +
  • + + Migrate + +
  • + {{/if}} {{#if this.showSubscriptionTab}}
  • @@ -161,10 +168,10 @@
  • {{/if}} - {{#if this.showMigrateTab}} -
  • - - Migrate + {{#if this.showPaymentDetailsTab}} +
  • + + Payment details
  • {{/if}} diff --git a/app/templates/organization/payment_details.hbs b/app/templates/organization/payment_details.hbs new file mode 100644 index 0000000000..c3658366a6 --- /dev/null +++ b/app/templates/organization/payment_details.hbs @@ -0,0 +1,5 @@ +
    + +
    diff --git a/tests/acceptance/profile/billing-test.js b/tests/acceptance/profile/billing-test.js index e9c0c59121..d2b550d87a 100644 --- a/tests/acceptance/profile/billing-test.js +++ b/tests/acceptance/profile/billing-test.js @@ -401,9 +401,6 @@ module('Acceptance | profile/billing', function (hooks) { assert.equal(profilePage.billing.plan.name, `${this.defaultV2Plan.name}`); assert.dom(profilePage.billing.plan.description.scope).hasTextContaining(`${this.defaultV2Plan.privateCredits * (this.defaultV2Plan.isAnnual ? 12 : 1)} Credits`); - - assert.equal(profilePage.billing.userDetails.text, 'contact name User Name billing email user@email.com'); - assert.equal(profilePage.billing.billingDetails.text, 'address Rigaerstraße 8 city Berlin post code 10987 country Germany'); }); test('view billing on an incomplete stripe plan', async function (assert) { @@ -973,9 +970,6 @@ module('Acceptance | profile/billing', function (hooks) { assert.equal(profilePage.billing.plan.name, `${this.defaultV2Plan.name}`); assert.dom(profilePage.billing.plan.description.scope).hasTextContaining(`${this.defaultV2Plan.privateCredits * (this.defaultV2Plan.isAnnual ? 12 : 1)} Credits`); - - assert.equal(profilePage.billing.userDetails.text, 'contact name John Doe company name Travis billing email john@doe.com'); - assert.equal(profilePage.billing.billingDetails.text, 'address 15 Olalubi street city Berlin post code 353564 country Germany vat id 356463'); }); test('logs an exception when there is a subscription without a plan and handles unknowns', async function (assert) { @@ -1304,9 +1298,6 @@ module('Acceptance | profile/billing', function (hooks) { assert.equal(profilePage.billing.plan.name, `${this.defaultV2Plan.name}`); assert.dom(profilePage.billing.plan.description.scope).hasTextContaining(`${this.defaultV2Plan.privateCredits * (this.defaultV2Plan.isAnnual ? 12 : 1)} Credits`); - - assert.equal(profilePage.billing.userDetails.text, 'contact name John Doe company name Travis billing email john@doe.com'); - assert.equal(profilePage.billing.billingDetails.text, 'address 15 Olalubi street city Berlin post code 353564 country Germany vat id 356463'); }); test('view billing tab when no organization subscription should fill form and transition to payment', async function (assert) { @@ -1373,9 +1364,6 @@ module('Acceptance | profile/billing', function (hooks) { assert.equal(profilePage.billing.plan.name, `${this.defaultV2Plan.name}`); assert.dom(profilePage.billing.plan.description.scope).hasTextContaining(`${this.defaultV2Plan.privateCredits * (this.defaultV2Plan.isAnnual ? 12 : 1)} Credits`); - - assert.equal(profilePage.billing.userDetails.text, 'contact name John Doe company name Travis billing email john@doe.com'); - assert.equal(profilePage.billing.billingDetails.text, 'address 15 Olalubi street city Berlin post code 353564 country Germany vat id 356463'); }); test('create subscription with multiple emails', async function (assert) { @@ -1451,9 +1439,6 @@ module('Acceptance | profile/billing', function (hooks) { assert.equal(profilePage.billing.plan.name, `${this.defaultV2Plan.name}`); assert.dom(profilePage.billing.plan.description.scope).hasTextContaining(`${this.defaultV2Plan.privateCredits * (this.defaultV2Plan.isAnnual ? 12 : 1)} Credits`); - - assert.equal(profilePage.billing.userDetails.text, 'contact name John Doe company name Travis billing email joe@jane.com jane@email.com joe@email.com doe@email.com'); - assert.equal(profilePage.billing.billingDetails.text, 'address 15 Olalubi street city Berlin post code 353564 country Germany vat id 356463'); }); test('view plan with manual subscription', async function (assert) { diff --git a/tests/integration/components/billing/invoices-test.js b/tests/integration/components/billing/invoices-test.js index 74c6380f2e..1e6778b49a 100644 --- a/tests/integration/components/billing/invoices-test.js +++ b/tests/integration/components/billing/invoices-test.js @@ -90,7 +90,7 @@ module('Integration | Component | billing-invoices', function (hooks) { />` ); - assert.dom('h3').hasText('Invoice history'); + assert.dom('h3').hasText('Payment history'); assert.dom('[data-test-help-text]').containsText('Having trouble with your invoices?'); assert.dom('[data-test-help-text] a').containsText('We’re happy to help'); assert.dom('[data-test-table-header-row] th').exists({ count: 5 }); @@ -134,7 +134,7 @@ module('Integration | Component | billing-invoices', function (hooks) { />` ); - assert.dom('h3').hasText('Invoice history'); + assert.dom('h3').hasText('Payment history'); assert.dom('[data-test-help-text]').containsText('Having trouble with your invoices?'); assert.dom('[data-test-help-text] a').containsText('We’re happy to help'); assert.dom('[data-test-table-header-row] th').exists({ count: 5 }); From 9cfef7e625e2367c232c29cf1c19ee4033b5f2d7 Mon Sep 17 00:00:00 2001 From: AndriiMysko Date: Thu, 9 Feb 2023 13:46:36 +0200 Subject: [PATCH 03/43] Antifraud for v1 plans --- app/components/billing/payment-details-tab.js | 7 ++----- app/components/profile-nav.js | 6 +++--- app/templates/components/billing/summary.hbs | 10 ---------- tests/acceptance/profile/billing-test.js | 12 ------------ 4 files changed, 5 insertions(+), 30 deletions(-) diff --git a/app/components/billing/payment-details-tab.js b/app/components/billing/payment-details-tab.js index df5dff3dc0..086a5a155a 100644 --- a/app/components/billing/payment-details-tab.js +++ b/app/components/billing/payment-details-tab.js @@ -90,7 +90,8 @@ export default Component.extend({ if (token) { paymentDetails['token'] = token.id; } - yield this.api.patch(`/v2_subscription/${subscription.id}/payment_details`, { + const endpoint = this.isV2SubscriptionEmpty ? 'subscription' : 'v2_subscription'; + yield this.api.patch(`/${endpoint}/${subscription.id}/payment_details`, { data: paymentDetails }); if (stripeElement) { @@ -144,10 +145,6 @@ export default Component.extend({ actions: { complete(stripeElement) { - if (!this.enableSubmit || this.disableForm) { - stripeElement.clear(); - return; - } this.set('stripeElement', stripeElement); }, modifyNameOnCard(value) { diff --git a/app/components/profile-nav.js b/app/components/profile-nav.js index 223a37d29f..3302218525 100644 --- a/app/components/profile-nav.js +++ b/app/components/profile-nav.js @@ -68,11 +68,11 @@ export default Component.extend({ const isEnterprise = this.features.get('enterpriseVersion'); return !isEnterprise && !isAssemblaUser && !!billingEndpoint; }), - showPaymentDetailsTab: computed('showSubscriptionTab', 'isOrganization', 'isOrganizationAdmin', 'model.hasV2Subscription', function () { + showPaymentDetailsTab: computed('showSubscriptionTab', 'isOrganization', 'isOrganizationAdmin', function () { if (this.isOrganization) { - return this.showSubscriptionTab && this.model.hasV2Subscription && this.isOrganizationAdmin; + return this.showSubscriptionTab && this.isOrganizationAdmin; } else { - return this.showSubscriptionTab && this.model.hasV2Subscription; + return this.showSubscriptionTab; } }), showPlanUsageTab: and('showSubscriptionTab', 'model.hasCredits'), diff --git a/app/templates/components/billing/summary.hbs b/app/templates/components/billing/summary.hbs index d678562d45..2ee7a5700f 100644 --- a/app/templates/components/billing/summary.hbs +++ b/app/templates/components/billing/summary.hbs @@ -86,13 +86,3 @@ {{/if}}

    -{{#if (and this.subscription.isStripe (not this.showPlansSelector))}} - - - -
    -{{/if}} diff --git a/tests/acceptance/profile/billing-test.js b/tests/acceptance/profile/billing-test.js index d2b550d87a..43b3a15d2d 100644 --- a/tests/acceptance/profile/billing-test.js +++ b/tests/acceptance/profile/billing-test.js @@ -168,8 +168,6 @@ module('Acceptance | profile/billing', function (hooks) { assert.ok(profilePage.billing.marketplaceButton.isHidden); assert.equal(profilePage.billing.plan.name, 'Small Business1 plan active'); - assert.equal(profilePage.billing.userDetails.text, 'contact name User Name company name Travis CI GmbH billing email user@email.com'); - assert.equal(profilePage.billing.billingDetails.text, 'address Rigaerstraße 8 city Berlin post code 10987 country Germany vat id 12345'); assert.dom(profilePage.billing.planMessage.scope).hasText('Valid until June 19, 2018'); assert.equal(profilePage.billing.creditCardNumber.text, '•••• •••• •••• 1919'); @@ -369,8 +367,6 @@ module('Acceptance | profile/billing', function (hooks) { percySnapshot(assert); assert.ok(profilePage.billing.marketplaceButton.isHidden); - assert.equal(profilePage.billing.userDetails.text, 'contact name User Name company name Travis CI GmbH billing email user@email.com'); - assert.equal(profilePage.billing.billingDetails.text, 'address Rigaerstraße 8 city Berlin post code 10987 country Germany vat id 12345'); await profilePage.billing.changePlanResubscribe.click(); @@ -413,8 +409,6 @@ module('Acceptance | profile/billing', function (hooks) { assert.equal(profilePage.billing.plan.name, 'Small Business1 plan incomplete'); assert.ok(profilePage.billing.marketplaceButton.isHidden); - assert.equal(profilePage.billing.userDetails.text, 'contact name User Name company name Travis CI GmbH billing email user@email.com'); - assert.equal(profilePage.billing.billingDetails.text, 'address Rigaerstraße 8 city Berlin post code 10987 country Germany vat id 12345'); }); test('cancel a stripe plan', async function (assert) { @@ -438,11 +432,8 @@ module('Acceptance | profile/billing', function (hooks) { assert.equal(profilePage.billing.plan.name, 'Small Business1 plan canceled'); assert.equal(profilePage.billing.planMessage.text, `Expires ${momentFromNow} on June 19`); - assert.equal(profilePage.billing.userDetails.text, 'contact name User Name company name Travis CI GmbH billing email user@email.com'); - assert.equal(profilePage.billing.billingDetails.text, 'address Rigaerstraße 8 city Berlin post code 10987 country Germany vat id 12345'); assert.dom(profilePage.billing.planMessage.scope).hasText(`Expires ${momentFromNow} on June 19`); - assert.equal(profilePage.billing.creditCardNumber.text, '•••• •••• •••• 1919'); assert.equal(profilePage.billing.price.text, '$69'); assert.equal(profilePage.billing.period.text, '/month'); @@ -468,11 +459,8 @@ module('Acceptance | profile/billing', function (hooks) { assert.equal(profilePage.billing.plan.name, 'Small Business1 plan canceled'); assert.equal(profilePage.billing.planMessage.text, `Expires ${momentFromNow} on June 19`); - assert.equal(profilePage.billing.userDetails.text, 'contact name User Name company name Travis CI GmbH billing email user@email.com'); - assert.equal(profilePage.billing.billingDetails.text, 'address Rigaerstraße 8 city Berlin post code 10987 country Germany vat id 12345'); assert.dom(profilePage.billing.planMessage.scope).hasText(`Expires ${momentFromNow} on June 19`); - assert.equal(profilePage.billing.creditCardNumber.text, '•••• •••• •••• 1919'); assert.equal(profilePage.billing.price.text, '$69'); assert.equal(profilePage.billing.period.text, '/month'); }); From 6376286b3a05c4c717bae18b671feb82607d9349 Mon Sep 17 00:00:00 2001 From: AndriiMysko Date: Fri, 10 Feb 2023 11:02:15 +0200 Subject: [PATCH 04/43] Adjust tests --- tests/acceptance/profile/billing-test.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/acceptance/profile/billing-test.js b/tests/acceptance/profile/billing-test.js index 43b3a15d2d..5aae2c28b6 100644 --- a/tests/acceptance/profile/billing-test.js +++ b/tests/acceptance/profile/billing-test.js @@ -131,7 +131,7 @@ module('Acceptance | profile/billing', function (hooks) { this.coupons = this.server.createList('coupon', 3); }); - test('view billing information with invoices', async function (assert) { + skip('view billing information with invoices', async function (assert) { this.subscription.createInvoice({ id: '1919', created_at: new Date(1919, 4, 15), @@ -189,7 +189,7 @@ module('Acceptance | profile/billing', function (hooks) { }); }); - test('view billing information with invoices year changes correctly', async function (assert) { + skip('view billing information with invoices year changes correctly', async function (assert) { this.subscription.createInvoice({ id: '2009', @@ -312,7 +312,7 @@ module('Acceptance | profile/billing', function (hooks) { assert.dom('[data-test-stripe-discount]').hasText('Discount: $10 off until September 2018'); }); - test('edit subscription contact updates user billing info', async function (assert) { + skip('edit subscription contact updates user billing info', async function (assert) { await profilePage.visit(); await profilePage.billing.visit(); @@ -337,7 +337,7 @@ module('Acceptance | profile/billing', function (hooks) { assert.equal(profilePage.billing.userDetails.text, 'contact name John Doe company name Travis billing email joe@jane.com jane@email.com'); }); - test('edit subscription billing updates user billing info', async function (assert) { + skip('edit subscription billing updates user billing info', async function (assert) { await profilePage.visit(); await profilePage.billing.visit(); From 5de968c4a235a4709d5d0b11e7f499814a040fdc Mon Sep 17 00:00:00 2001 From: AndriiMysko Date: Fri, 10 Feb 2023 11:15:43 +0200 Subject: [PATCH 05/43] fix for lack of first_sync for assembla --- app/services/auth.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/services/auth.js b/app/services/auth.js index 9d28fb9da7..fc6ea6c4ce 100644 --- a/app/services/auth.js +++ b/app/services/auth.js @@ -16,7 +16,7 @@ import { } from '@ember/object/computed'; import { getOwner } from '@ember/application'; import config from 'travis/config/environment'; -import { task } from 'ember-concurrency'; +import { task, didCancel } from 'ember-concurrency'; import { availableProviders, vcsConfigByUrlPrefixOrType } from 'travis/utils/vcs'; const { authEndpoint, apiEndpoint } = config; @@ -183,7 +183,9 @@ export default Service.extend({ Travis.trigger('user:refreshed', currentUser); }) .catch(error => { - throw new Error(error); + if (!didCancel(error)) { + throw new Error(error); + } }); } catch (error) { this.signOut(false); From 62e6d2818d84a84853574e7682af2fcbeaa9b964 Mon Sep 17 00:00:00 2001 From: AndriiMysko Date: Fri, 10 Feb 2023 11:17:54 +0200 Subject: [PATCH 06/43] added hosted billing help --- app/controllers/help.js | 9 +++- app/templates/components/billing-manual.hbs | 2 +- app/templates/components/billing/invoices.hbs | 2 +- .../components/billing/summary-v2.hbs | 2 +- .../components/manual-subscription-help.hbs | 2 +- app/templates/help.hbs | 52 +++++++++++++++---- config/environment.js | 3 ++ 7 files changed, 56 insertions(+), 16 deletions(-) diff --git a/app/controllers/help.js b/app/controllers/help.js index 5d70e5f1f6..5891aeb47c 100644 --- a/app/controllers/help.js +++ b/app/controllers/help.js @@ -15,14 +15,15 @@ const UTM_SOURCE = 'help-page'; const UTM_MEDIUM = 'travisweb'; const UTM_PARAMS = `?utm_source=${UTM_SOURCE}&utm_medium=${UTM_MEDIUM}`; -const { docs, community, docker, node, multiOS, noRun, tutorial } = config.urls; +const { docs, community, docker, node, multiOS, noRun, tutorial, billingOverview, autoRefill, billingFaq } = config.urls; export default Controller.extend({ auth: service(), features: service(), - queryParams: ['anchor', 'page'], + queryParams: ['anchor', 'page', 'billing'], anchor: ANCHOR.TOP, + billing: 'false', page: '', isSignedIn: reads('auth.signedIn'), @@ -31,6 +32,7 @@ export default Controller.extend({ toDocs: equal('anchor', ANCHOR.DOCS), toCommunity: equal('anchor', ANCHOR.COMMUNITY), toForm: equal('anchor', ANCHOR.FORM), + isBilling: equal('billing', 'true'), utmParams: computed(() => UTM_PARAMS), docsUrl: computed(() => `${docs}${UTM_PARAMS}`), @@ -39,6 +41,9 @@ export default Controller.extend({ multiOsUrl: computed(() => `${multiOS}${UTM_PARAMS}`), noRunUrl: computed(() => `${noRun}${UTM_PARAMS}`), tutorialUrl: computed(() => `${tutorial}${UTM_PARAMS}`), + billingOverviewUrl: computed(() => `${billingOverview}${UTM_PARAMS}`), + autoRefillUrl: computed(() => `${autoRefill}${UTM_PARAMS}`), + faqUrl: computed(() => `${billingFaq}${UTM_PARAMS}`), communityUrl: computed(() => `${community}/top${UTM_PARAMS}`), diff --git a/app/templates/components/billing-manual.hbs b/app/templates/components/billing-manual.hbs index 6c2434de07..923055ba68 100644 --- a/app/templates/components/billing-manual.hbs +++ b/app/templates/components/billing-manual.hbs @@ -27,7 +27,7 @@ This manual subscription is paid to Travis CI by bank transfer. If you have any questions or would like to update your plan, - + contact our support team. diff --git a/app/templates/components/billing/invoices.hbs b/app/templates/components/billing/invoices.hbs index 054044c673..86a9d28bcd 100644 --- a/app/templates/components/billing/invoices.hbs +++ b/app/templates/components/billing/invoices.hbs @@ -4,7 +4,7 @@

    Having trouble with your invoices? - + We’re happy to help

    diff --git a/app/templates/components/billing/summary-v2.hbs b/app/templates/components/billing/summary-v2.hbs index b46755c7dd..e2a4712d9b 100644 --- a/app/templates/components/billing/summary-v2.hbs +++ b/app/templates/components/billing/summary-v2.hbs @@ -278,7 +278,7 @@
    This manual subscription is paid to Travis CI by bank transfer. If you have any questions or would like to update your plan, - + contact our support team.
    diff --git a/app/templates/components/manual-subscription-help.hbs b/app/templates/components/manual-subscription-help.hbs index 9be74fbc0f..2662eb1923 100644 --- a/app/templates/components/manual-subscription-help.hbs +++ b/app/templates/components/manual-subscription-help.hbs @@ -1,6 +1,6 @@

    If you have any questions or would like to update your plan, please - + contact our support team.

    diff --git a/app/templates/help.hbs b/app/templates/help.hbs index 8ec86e7287..0e41368012 100644 --- a/app/templates/help.hbs +++ b/app/templates/help.hbs @@ -40,6 +40,36 @@
    + + {{#if this.isBilling}} +
    +

    + Hosted Billing +

    +
    + If you have problems with billing, please check the Billing section + in the documentation or fill out the form below +
    +
    +
      +
    • + + Billing Overview + +
    • +
    • + + Auto-refill + +
    • +
    • + + FAQ + +
    • +
    +
    + {{else}}

    Handy Resources @@ -79,8 +109,20 @@ Check Our Documentation

    + {{/if}} + + {{#if this.toForm}} + + {{/if}} + + + - - {{#if this.toForm}} - - {{/if}} - - diff --git a/config/environment.js b/config/environment.js index eb84ea17a4..6ca555d61a 100644 --- a/config/environment.js +++ b/config/environment.js @@ -115,6 +115,9 @@ module.exports = function (environment) { planDocs: 'https://docs.travis-ci.com/user/billing-overview/', planCreditDocs: 'https://docs.travis-ci.com/user/billing-overview/#usage---credits', planUsersDocs: 'https://docs.travis-ci.com/user/billing-overview/#usage---user-licenses', + billingOverview: 'https://docs.travis-ci.com/user/billing-overview/', + autoRefill: 'https://docs.travis-ci.com/user/billing-autorefill/', + billingFaq: 'https://docs.travis-ci.com/user/billing-faq/', }, endpoints: {}, githubApps: false, From d97e0618b25a555eeaa719a4d391fdcebb596a24 Mon Sep 17 00:00:00 2001 From: Jullianos Date: Tue, 31 Jan 2023 15:55:11 +0100 Subject: [PATCH 07/43] Add the new macOS information section in select-plan and modify billing scss styles --- app/styles/app/layouts/billing.scss | 8 ++++++++ app/templates/components/billing/select-plan.hbs | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/app/styles/app/layouts/billing.scss b/app/styles/app/layouts/billing.scss index 0d9b1e6e37..d18a7ede22 100644 --- a/app/styles/app/layouts/billing.scss +++ b/app/styles/app/layouts/billing.scss @@ -1878,3 +1878,11 @@ } } } + +.mac_os_additional_credits { + font-size: 12px; + + .documentation-link { + color: #666; + } +} diff --git a/app/templates/components/billing/select-plan.hbs b/app/templates/components/billing/select-plan.hbs index a9b60de53f..da5b252a05 100644 --- a/app/templates/components/billing/select-plan.hbs +++ b/app/templates/components/billing/select-plan.hbs @@ -93,6 +93,12 @@

    Looking for more credits, users or VM sizes? Contact our Sales Team

    +

    + macOS builds require the purchase of credits. For more information, please review our + + documentation + . +

    {{#if this.showCalculator}} Date: Mon, 13 Feb 2023 11:19:46 +0200 Subject: [PATCH 08/43] Adjust manual and github plans view --- app/components/billing/payment-details-tab.js | 10 ++++++++-- app/components/profile-nav.js | 6 +++--- app/models/owner.js | 5 +++++ app/models/subscription.js | 4 ++++ app/models/v2-subscription.js | 4 ++++ .../components/billing/payment-details-tab.hbs | 5 +++++ app/templates/components/billing/summary-v2.hbs | 1 - 7 files changed, 29 insertions(+), 6 deletions(-) diff --git a/app/components/billing/payment-details-tab.js b/app/components/billing/payment-details-tab.js index 086a5a155a..ed2cdf711a 100644 --- a/app/components/billing/payment-details-tab.js +++ b/app/components/billing/payment-details-tab.js @@ -57,13 +57,19 @@ export default Component.extend({ return paymentChangesBlockCaptcha || paymentChangesBlockCredit; }), - billingInfo: reads('subscription.billingInfo'), + subscriptionLoaded: computed('subscription', function () { + return !!this.subscription; + }), + + billingInfo: computed('subscription', 'subscription.billingInfo', function () { + return this.subscription ? this.subscription.get('billingInfo') : null; + }), country: reads('billingInfo.country'), firstName: reads('billingInfo.firstName'), lastName: reads('billingInfo.lastName'), nameOnCard: computed('firstName', 'lastName', function () { - return `${this.firstName} ${this.lastName}`; + return `${this.firstName || ''} ${this.lastName || ''}`; }), isLoading: reads('updatePaymentDetails.isRunning'), diff --git a/app/components/profile-nav.js b/app/components/profile-nav.js index e7c05c19ee..e11fd17eaa 100644 --- a/app/components/profile-nav.js +++ b/app/components/profile-nav.js @@ -68,11 +68,11 @@ export default Component.extend({ const isEnterprise = this.features.get('enterpriseVersion'); return !isEnterprise && !isAssemblaUser && !!billingEndpoint; }), - showPaymentDetailsTab: computed('showSubscriptionTab', 'isOrganization', 'isOrganizationAdmin', function () { + showPaymentDetailsTab: computed('showSubscriptionTab', 'isOrganization', 'isOrganizationAdmin', 'model.isNotGithubOrManual', function () { if (this.isOrganization) { - return this.showSubscriptionTab && this.isOrganizationAdmin; + return this.showSubscriptionTab && this.isOrganizationAdmin && this.model.get('isNotGithubOrManual'); } else { - return this.showSubscriptionTab; + return this.showSubscriptionTab && this.model.get('isNotGithubOrManual'); } }), showPlanUsageTab: and('showSubscriptionTab', 'model.hasCredits'), diff --git a/app/models/owner.js b/app/models/owner.js index 70694fcc49..99164198d5 100644 --- a/app/models/owner.js +++ b/app/models/owner.js @@ -208,6 +208,11 @@ export default VcsEntity.extend({ return this.hasV2Subscription ? this.v2subscription.get('hasCredits') : false; }), + isNotGithubOrManual: computed('hasV2Subscription', 'v2subscription', 'subscription', function () { + if (!this.v2subscription && !this.subscription) return false; + return this.hasV2Subscription ? this.v2subscription.get('isNotGithubOrManual') : this.subscription.get('isNotGithubOrManual'); + }), + trial: computed('accounts.trials.@each.{created_at,owner,hasTrial}', 'login', function () { let trials = this.get('accounts.trials') || []; let login = this.login; diff --git a/app/models/subscription.js b/app/models/subscription.js index 4726a2cdcc..241233fde6 100644 --- a/app/models/subscription.js +++ b/app/models/subscription.js @@ -119,6 +119,10 @@ export default Model.extend({ return (isManual && (date > validToDate)); }), + isNotGithubOrManual: computed('source', function () { + return this.source !== 'github' && this.source !== 'manual'; + }), + chargeUnpaidInvoices: task(function* () { return yield this.api.post(`/subscription/${this.id}/pay`); }).drop(), diff --git a/app/models/v2-subscription.js b/app/models/v2-subscription.js index 379afacafe..4742eb4de5 100644 --- a/app/models/v2-subscription.js +++ b/app/models/v2-subscription.js @@ -145,6 +145,10 @@ export default Model.extend({ }), hasCredits: or('hasCreditAddons', 'hasOSSCreditAddons'), + isNotGithubOrManual: computed('source', function () { + return this.source !== 'github' && this.source !== 'manual'; + }), + priceInCents: reads('plan.planPrice'), validateCouponResult: reads('validateCoupon.last.value'), diff --git a/app/templates/components/billing/payment-details-tab.hbs b/app/templates/components/billing/payment-details-tab.hbs index 4cd3d65aab..77163bcc69 100644 --- a/app/templates/components/billing/payment-details-tab.hbs +++ b/app/templates/components/billing/payment-details-tab.hbs @@ -1,4 +1,5 @@
    + {{#if this.subscriptionLoaded}}

    You can update your payment method details here. Your credit card will be validated by placing test fee - $1, which will be returned to you within a week. @@ -223,4 +224,8 @@ /> {{/if}} {{/if}} + + {{else}} + + {{/if}}

    diff --git a/app/templates/components/billing/summary-v2.hbs b/app/templates/components/billing/summary-v2.hbs index e2a4712d9b..5011f8da71 100644 --- a/app/templates/components/billing/summary-v2.hbs +++ b/app/templates/components/billing/summary-v2.hbs @@ -275,7 +275,6 @@ {{/if}} {{#if this.subscription.isManual}} -
    This manual subscription is paid to Travis CI by bank transfer. If you have any questions or would like to update your plan, From 6e2a22a426f0dc775da886507bf3c384d330cb8a Mon Sep 17 00:00:00 2001 From: AndriiMysko Date: Thu, 16 Feb 2023 11:27:02 +0200 Subject: [PATCH 09/43] Fix local registration checkbox --- app/components/billing/payment-details-tab.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/components/billing/payment-details-tab.js b/app/components/billing/payment-details-tab.js index ed2cdf711a..7e3630a2ae 100644 --- a/app/components/billing/payment-details-tab.js +++ b/app/components/billing/payment-details-tab.js @@ -71,6 +71,7 @@ export default Component.extend({ nameOnCard: computed('firstName', 'lastName', function () { return `${this.firstName || ''} ${this.lastName || ''}`; }), + hasLocalRegistration: reads('billingInfo.hasLocalRegistration'), isLoading: reads('updatePaymentDetails.isRunning'), From 773022e22bb58aa554ad78f3ea1f4059d4fa2f02 Mon Sep 17 00:00:00 2001 From: AndriiMysko Date: Thu, 23 Feb 2023 13:07:46 +0200 Subject: [PATCH 10/43] ui updates --- app/adapters/user.js | 2 +- app/components/billing/first-plan.js | 254 ++++++++++++ app/components/header-links.js | 2 + app/components/layouts/activation-section.js | 9 + app/components/layouts/activation.js | 9 + app/components/owner/repositories.js | 27 +- app/components/owner/wizard.js | 42 ++ app/components/profile-menu.js | 2 + app/components/top-bar.js | 1 + app/components/ui-kit/button-signin.js | 7 +- app/controllers/application.js | 12 + app/controllers/signup.js | 2 +- app/models/user.js | 4 + app/router.js | 1 + app/routes/account-activation.js | 37 ++ app/routes/application.js | 15 +- app/routes/first-sync.js | 15 +- app/services/auth.js | 13 + app/services/storage.js | 19 + app/services/wizard-state.js | 31 ++ app/styles/app.scss | 1 + app/styles/app/base.scss | 6 +- app/styles/app/layouts/activation.scss | 24 ++ app/styles/app/layouts/billing-plans.scss | 383 +++++++++++++++++ app/styles/app/layouts/billing.scss | 388 +----------------- app/styles/app/layouts/top.scss | 5 + app/styles/app/modules/buttons.scss | 15 + app/styles/app/modules/forms.scss | 4 + app/styles/app/modules/icons.scss | 8 + app/styles/app/modules/navigation.scss | 4 + app/templates/account-activation.hbs | 21 + .../components/billing/first-plan.hbs | 182 ++++++++ app/templates/components/header-links.hbs | 7 + .../components/layouts/activation-section.hbs | 3 + .../components/layouts/activation.hbs | 7 + app/templates/components/org-item.hbs | 5 + .../components/owner/repositories.hbs | 8 + app/templates/components/owner/wizard.hbs | 50 +++ app/templates/components/profile-menu.hbs | 8 +- app/templates/components/profile-nav.hbs | 3 +- app/templates/components/sync-button.hbs | 24 +- app/templates/components/top-bar.hbs | 4 +- app/templates/layouts/activation.hbs | 7 + public/images/svg/sync-icon.svg | 11 + 44 files changed, 1267 insertions(+), 415 deletions(-) create mode 100644 app/components/billing/first-plan.js create mode 100644 app/components/layouts/activation-section.js create mode 100644 app/components/layouts/activation.js create mode 100644 app/components/owner/wizard.js create mode 100644 app/routes/account-activation.js create mode 100644 app/services/wizard-state.js create mode 100644 app/styles/app/layouts/activation.scss create mode 100644 app/styles/app/layouts/billing-plans.scss create mode 100644 app/templates/account-activation.hbs create mode 100644 app/templates/components/billing/first-plan.hbs create mode 100644 app/templates/components/layouts/activation-section.hbs create mode 100644 app/templates/components/layouts/activation.hbs create mode 100644 app/templates/components/owner/wizard.hbs create mode 100644 app/templates/layouts/activation.hbs create mode 100644 public/images/svg/sync-icon.svg diff --git a/app/adapters/user.js b/app/adapters/user.js index f6d022f087..cfe93fa429 100644 --- a/app/adapters/user.js +++ b/app/adapters/user.js @@ -8,7 +8,7 @@ export default V3Adapter.extend({ assert('Invalid parameters for /user request', isQueryingCurrentUser || isUpdatingCurrentUser); - return `${this.urlPrefix()}/user`; + return `${this.urlPrefix()}/user?include=user.collaborator`; }, // This overrides the parent implementation to ignore the query parameters diff --git a/app/components/billing/first-plan.js b/app/components/billing/first-plan.js new file mode 100644 index 0000000000..08ebb99a73 --- /dev/null +++ b/app/components/billing/first-plan.js @@ -0,0 +1,254 @@ +import Component from '@ember/component'; +import { task } from 'ember-concurrency'; +import { inject as service } from '@ember/service'; +import { or, not, reads, filterBy, alias } from '@ember/object/computed'; +import { computed } from '@ember/object'; +import { typeOf } from '@ember/utils'; +import config from 'travis/config/environment'; +import { countries,states,zeroVatThresholdCountries, nonZeroVatThresholdCountries, stateCountries } from 'travis/utils/countries'; + +export default Component.extend({ + stripe: service(), + store: service(), + auth: service(), + accounts: service(), + flashes: service(), + metrics: service(), + storage: service(), + router: service(), + wizard: service('wizard-state'), + countries, + states: computed('country', function() { + const { country } = this; + return states[country]; + }), + + user: null, + account: alias('accounts.user'), + stripeElement: null, + stripeLoading: false, + couponId: null, + options: config.stripeOptions, + showSwitchToFreeModal: false, + showPlanSwitchWarning: false, + availablePlans: reads('account.eligibleV2Plans'), + defaultPlans: filterBy('availablePlans', 'trialPlan'), + defaultPlanId: reads('defaultPlans.firstObject.id'), + showCancelButton: false, + travisTermsUrl: "/terms", + travisPolicyUrl: "/policy", + subscription: null, + vatId: null, + + displayedPlans: reads('availablePlans'), + + selectedPlan: computed('displayedPlans.[].id', 'defaultPlanId', function () { + let plan = this.storage.selectedPlanId; + if (plan == null) { + plan = this.defaultPlanId; + } + + return this.displayedPlans.findBy('id', plan); + }), + + isTrial: computed('selectedPlan', function () { + let plan = this.selectedPlan; + return plan ? plan.isTrial : true; + }), + + hasLocalRegistration: false, + firstName: "", + lastName: "", + company: "", + address: "", + city: "", + country: "", + billingEmail: "", + billingEmails: computed('billingEmail', function () { + return (this.billingEmail || '').split(','); + }), + + states: computed('country', function () { + const { country } = this; + + return states[country]; + }), + + isStateCountry: computed('country', function () { + const { country } = this; + + return !!country && stateCountries.includes(country); + }), + + isZeroVatThresholdCountry: computed('country', function () { + const { country } = this; + return !!country && zeroVatThresholdCountries.includes(country); + }), + + isNonZeroVatThresholdCountry: computed('country', function () { + const { country } = this; + return !!country && nonZeroVatThresholdCountries.includes(country); + }), + + isVatMandatory: computed('isNonZeroVatThresholdCountry', 'hasLocalRegistration', function () { + const { isNonZeroVatThresholdCountry, isZeroVatThresholdCountry, hasLocalRegistration } = this; + return isZeroVatThresholdCountry || (isNonZeroVatThresholdCountry ? hasLocalRegistration : false); + }), + + showNonZeroVatConfirmation: reads('isNonZeroVatThresholdCountry'), + + showVatField: computed('country', 'isNonZeroVatThresholdCountry', 'hasLocalRegistration', function () { + const { country, isNonZeroVatThresholdCountry, hasLocalRegistration } = this; + return country && (isNonZeroVatThresholdCountry ? hasLocalRegistration : true); + }), + + isStateMandatory: reads('isStateCountry'), + + isLoading: false, + + isNewSubscription: not('subscription.id'), + + creditCardInfo: null, + creditCardOwner: null, + + creditCardInfoEmpty: computed('subscription.creditCardInfo', function () { + return !this.creditCardInfo.lastDigits; + }), + + getPriceInfo: computed('selectedPlan', function() { + let plan = this.selectedPlan; + console.log(plan); + return '$' + plan.startingPrice + (plan.isAnnual ? ' annualy' :' monthly'); + }), + + getActivateButtonText: computed('selectedPlan', function() { + let text = "Verify Your Account"; + let plan = this.selectedPlan; + if (plan && !plan.isTrial) { + text = "Activate " + plan.name; + } + return text; + }), + + canActivate: computed('country','zipCode','address','creditCardOwner','city', 'stripeElement', 'billingEmail', function() { + return this.billingEmail && this.country && this.zipCode && this.address && this.creditCardOwner && this.stripeElement && this.city; + }), + + + + createSubscription: task(function* () { + this.metrics.trackEvent({ + action: 'Pay Button Clicked', + category: 'Subscription', + }); + const { stripeElement, account, selectedPlan } = this; + try { + this.set('subscription',this.newV2Subscription()); + + const { token } = yield this.stripe.createStripeToken.perform(stripeElement); + + if (token) { + const organizationId = null;// account.type === 'organization' ? +(account.id) : null; + const plan = selectedPlan && selectedPlan.id && this.store.peekRecord('v2-plan-config', selectedPlan.id); + const org = organizationId && this.store.peekRecord('organization', organizationId); + + this.subscription.setProperties({ + organization: org, + plan: plan, + v1SubscriptionId: this.v1SubscriptionId, + }); + if (!this.subscription.id) { + this.subscription.creditCardInfo.setProperties({ + token: token.id, + lastDigits: token.card.last4 + }); + this.subscription.setProperties({ + coupon: this.couponId + }); + const { clientSecret } = yield this.subscription.save(); + yield this.stripe.handleStripePayment.perform(clientSecret); + } else { + yield this.subscription.creditCardInfo.updateToken.perform({ + subscriptionId: this.subscription.id, + tokenId: token.id, + tokenCard: token.card + }); + yield this.subscription.save(); + yield this.subscription.changePlan.perform(selectedPlan.id, this.couponId); + yield this.accounts.fetchV2Subscriptions.perform(); + yield this.retryAuthorization.perform(); + } + this.metrics.trackEvent({ button: 'pay-button' }); + this.storage.clearBillingData(); + this.storage.clearSelectedPlanId(); + this.storage.wizardStep = 2; + this.wizard.update.perform(2); + this.router.transitionTo('/account/repositories'); + + } + this.flashes.success("Your account has been successfully activated"); + } catch (error) { + this.handleError(); + } + }).drop(), + + newV2Subscription() { + const plan = this.store.createRecord('v2-plan-config'); + const billingInfo = this.store.createRecord('v2-billing-info'); + const creditCardInfo = this.store.createRecord('v2-credit-card-info'); + billingInfo.setProperties({ + firstName: this.firstName, + lastName: this.lastName, + address: this.address, + city: this.city, + zipCode: this.zipCode, + country: this.country, + state: this.state, + billingEmail: this.billingEmail, + hasLocalRegistration: this.hasLocalRegistration, + vatId: this.vatId + }); + creditCardInfo.setProperties({ + token: '', + lastDigits: '' + }); + return this.store.createRecord('v2-subscription', { + billingInfo, + plan, + creditCardInfo, + }); + }, + handleError() { + let message = this.get('selectedPlan.isTrial') + ? 'Credit card verification failed, please try again or use a different card.' + : 'An error occurred when creating your subscription. Please try again.'; + this.flashes.error(message); + }, + + actions: { + complete(stripeElement) { + this.set('stripeElement', stripeElement); + }, + + clearCreditCardData() { + this.subscription.set('creditCardInfo', null); + }, + changePlan() { + this.set('showPlansSelector', true); + }, + closePlansModal() { + this.set('showPlansSelector', false); + }, + verifyAccount() { + + }, + subscribe() { + this.createSubscription.perform(); + }, + changeCountry(country) { + this.set('country', country); + this.hasLocalRegistration = false; + } + + } +}); diff --git a/app/components/header-links.js b/app/components/header-links.js index ef949a4090..e30a3737cd 100644 --- a/app/components/header-links.js +++ b/app/components/header-links.js @@ -18,6 +18,8 @@ export default Component.extend({ externalLinks: service(), multiVcs: service(), + isActivation: false, + deploymentVersion: computed(function () { if (window && window.location) { const hostname = window.location.hostname; diff --git a/app/components/layouts/activation-section.js b/app/components/layouts/activation-section.js new file mode 100644 index 0000000000..29551adfee --- /dev/null +++ b/app/components/layouts/activation-section.js @@ -0,0 +1,9 @@ +import Component from '@ember/component'; + +export default Component.extend({ + tagName: 'activation-section', + classNames: ['layout-activation-section'], + classNameBindings: ['hasBackground:layout-activation-section--with-bg'], + + hasBackground: false +}); diff --git a/app/components/layouts/activation.js b/app/components/layouts/activation.js new file mode 100644 index 0000000000..cb6b2a82c6 --- /dev/null +++ b/app/components/layouts/activation.js @@ -0,0 +1,9 @@ +import Component from '@ember/component'; + +export default Component.extend({ + tagName: '', + + useTailwind: false, + isTopBarWhite: false, + isFlush: false, +}); diff --git a/app/components/owner/repositories.js b/app/components/owner/repositories.js index 5c136d07ac..e866b2d9f2 100644 --- a/app/components/owner/repositories.js +++ b/app/components/owner/repositories.js @@ -23,6 +23,8 @@ const { appName, migrationRepositoryCountLimit } = config.githubApps; export default Component.extend({ features: service(), store: service(), + storage: service(), + wizard: service('wizard-state'), owner: null, @@ -77,6 +79,18 @@ export default Component.extend({ return `https://travis-ci.com/${path}`; }), + wizardStep: reads('storage.wizardStep'), + wizardState: reads('wizard.state'), + + showWizard: computed('wizardStep', function () { + console.log(`STEP:`); + console.log( this.wizardStep); + let state = this.wizardStep; + + return state && state <= 3; + }), + + appsActivationURL: computed('owner.githubId', function () { let githubId = this.get('owner.githubId'); return `${config.githubAppsEndpoint}/${appName}/installations/new/permissions?suggested_target_id=${githubId}`; @@ -130,5 +144,16 @@ export default Component.extend({ window.location.href = `${config.githubAppsEndpoint}/${appName}/installations/new/permissions` + `?suggested_target_id=${this.owner.githubId}&${githubQueryParams}`; - }) + }), + + closeWizard: task(function* () { + this.set('showWizard', false); + yield this.wizard.delete.perform(); + }).drop(), + + actions: { + onWizardClose() { + this.closeWizard.perform(); + } + } }); diff --git a/app/components/owner/wizard.js b/app/components/owner/wizard.js new file mode 100644 index 0000000000..be56f83e0a --- /dev/null +++ b/app/components/owner/wizard.js @@ -0,0 +1,42 @@ +import Component from '@ember/component'; +import { inject as service } from '@ember/service'; +import { task } from 'ember-concurrency'; +import { computed } from '@ember/object'; +import { later } from '@ember/runloop'; +import { or, reads, filterBy } from '@ember/object/computed'; +import config from 'travis/config/environment'; + +const { docs, languages } = config.urls; + + +export default Component.extend({ + accounts: service(), + store: service(), + storage: service(), + wizard: service('wizard-state'), + + account: null, + wizardStep: null, + showWizard: null, + onClose: null, + travisDocsUrl: computed(() => `${docs}`), + travisBasicLanguageExamplesUrl: computed(() => `${languages}`), + showWizard: false, + + updateStep: task(function* (val) { + let step = parseInt(this.wizardStep) + val; + this.set('wizardStep', step); + yield this.wizard.update.perform(step); + + }).drop(), + + actions: { + nextStep() { + this.updateStep.perform(1); + if (this.wizardStep > 3) this.get('onClose') (); + }, + previousStep() { + this.updateStep.perform(-1); + } + } +}); diff --git a/app/components/profile-menu.js b/app/components/profile-menu.js index 99cd0507a3..123e3c1ed5 100644 --- a/app/components/profile-menu.js +++ b/app/components/profile-menu.js @@ -18,6 +18,7 @@ export default Component.extend({ features: service(), isMenuOpen: false, + isActivation: false, user: reads('auth.currentUser'), @@ -74,6 +75,7 @@ export default Component.extend({ }, toggleMenu() { + if(this.isActivation) return this.closeMenu(); if (this.isMenuOpen) { this.closeMenu(); } else { diff --git a/app/components/top-bar.js b/app/components/top-bar.js index 508c7a7248..8ca702897e 100644 --- a/app/components/top-bar.js +++ b/app/components/top-bar.js @@ -21,6 +21,7 @@ export default Component.extend(InViewportMixin, { isWhite: false, landingPage: false, isNavigationOpen: false, + isActivation: false, activeModel: null, model: reads('activeModel'), diff --git a/app/components/ui-kit/button-signin.js b/app/components/ui-kit/button-signin.js index f4dd5242d4..fa131d1464 100644 --- a/app/components/ui-kit/button-signin.js +++ b/app/components/ui-kit/button-signin.js @@ -54,7 +54,12 @@ export default Component.extend({ this.auth.switchAccount(this.account.id, this.auth.redirectUrl || '/'); } else { this.set('isLoading', true); - this.auth.signInWith(this.provider); + if(this.isSignup) { + this.auth.signUp(this.provider); + } + else { + this.auth.signInWith(this.provider); + } } } else { window.location.href = 'https://app.travis-ci.com/signin'; diff --git a/app/controllers/application.js b/app/controllers/application.js index f420352d16..171763c9aa 100644 --- a/app/controllers/application.js +++ b/app/controllers/application.js @@ -3,6 +3,7 @@ import { inject as service } from '@ember/service'; import { Promise } from 'rsvp'; import config from 'travis/config/environment'; import { later } from '@ember/runloop'; +import { reads } from '@ember/object/computed'; const { utmParametersResetDelay } = config.timing; @@ -11,6 +12,12 @@ export default Controller.extend({ metrics: service(), router: service(), utm: service(), + auth: service(), + storage: service(), + user: reads('auth.currentUser'), + queryParams: ['selectedPlanId'], + selectedPlanId: null, + trackPage(page) { page = page || this.router.currentURL || this.router.location.getURL(); @@ -40,6 +47,11 @@ export default Controller.extend({ init() { this._super(...arguments); this.router.on('routeDidChange', () => this.handleRouteChange()); + this.router.on('routeWillChange', (transition) => { + if(this.selectedPlanId) { + this.storage.selectedPlanId = this.selectedPlanId; + } + }); this.utm.capture(); } }); diff --git a/app/controllers/signup.js b/app/controllers/signup.js index 8034ef4c31..becfb35509 100644 --- a/app/controllers/signup.js +++ b/app/controllers/signup.js @@ -8,7 +8,7 @@ export default Controller.extend({ actions: { signIn(provider) { - this.auth.signInWith(provider); + this.auth.signUp(provider); }, }, }); diff --git a/app/models/user.js b/app/models/user.js index 6b93ee6a93..3f56b9dcf8 100644 --- a/app/models/user.js +++ b/app/models/user.js @@ -11,6 +11,7 @@ export default Owner.extend({ api: service(), accounts: service(), permissionsService: service('permissions'), + wizardStateService: service('wizardState'), email: attr('string'), emails: attr(), // list of all known user emails @@ -26,6 +27,7 @@ export default Owner.extend({ utmParams: attr(), confirmedAt: attr('date'), customKeys: attr(), + collaborator: attr('boolean'), type: 'user', @@ -45,6 +47,7 @@ export default Owner.extend({ adminPermissions: reads('permissionsService.admin'), pullPermissions: reads('permissionsService.pull'), pushPermissions: reads('permissionsService.push'), + wizardState: reads('wizardStateService.state'), hasAccessToRepo(repo) { let id = repo.get ? repo.get('id') : repo; @@ -92,6 +95,7 @@ export default Owner.extend({ this.schedulePoll(); } else { this.permissionsService.fetchPermissions.perform(); + this.wizardStateService.fetch.perform(); this.accounts.fetchOrganizations.perform(); this.applyReposFilter(); Travis.trigger('user:synced', this); diff --git a/app/router.js b/app/router.js index 413a8f9132..eed7e29f99 100644 --- a/app/router.js +++ b/app/router.js @@ -21,6 +21,7 @@ Router.map(function () { this.route('search', { path: '/search/:phrase' }); this.route('first_sync'); + this.route('account_activation'); this.route('insufficient_oauth_permissions'); this.route('signin'); this.route('signup'); diff --git a/app/routes/account-activation.js b/app/routes/account-activation.js new file mode 100644 index 0000000000..cbfc5d7955 --- /dev/null +++ b/app/routes/account-activation.js @@ -0,0 +1,37 @@ +import Route from '@ember/routing/route'; +import { inject as service } from '@ember/service'; +import SimpleLayoutRoute from 'travis/routes/simple-layout'; + +export default SimpleLayoutRoute.extend({ + auth: service(), + accounts: service(), + router: service(), + features: service(), + stripe: service(), + storage: service(), + wizardStateService: service('wizard-state'), + + activate() { + console.log("activate"); + this.storage.wizardStep = 1; + this.wizardStateService.update.perform(1); + }, + + deactivate() { + console.log("deactivate"); + let step = this.storage.wizardStep; + if (step == 2 || step == 3) this.transitionTo('/account/repositories'); + }, + + title: 'Travis CI - Select Plan', + + beforeModel() { + return this.stripe.load(); + }, + model() { + this.wizardStateService.fetch.perform(); + this.accounts.user.fetchV2Plans.perform(); + return this.accounts.user; + } + +}); diff --git a/app/routes/application.js b/app/routes/application.js index 8e945e9981..0c616b87dd 100644 --- a/app/routes/application.js +++ b/app/routes/application.js @@ -15,6 +15,11 @@ export default TravisRoute.extend(BuildFaviconMixin, { featureFlags: service(), flashes: service(), repositories: service(), + storage: service(), + wizard: service('wizard-state'), + queryParams: { + selectedPlanId: null, + }, needsAuth: false, @@ -32,8 +37,16 @@ export default TravisRoute.extend(BuildFaviconMixin, { return this.auth.autoSignIn(); }, - model() { + afterModel() { + console.log("APP AFTER CALLED"); + }, + + model(model, transition) { + if(model.selectedPlanId) { + this.storage.selectedPlanId = model.selectedPlanId; + } if (this.auth.signedIn) { + this.wizard.fetch.perform().then(() => {this.storage.wizardStep = this.wizard.state;}); return this.get('featureFlags.fetchTask').perform(); } }, diff --git a/app/routes/first-sync.js b/app/routes/first-sync.js index b40d26d2bc..afae3ca26d 100644 --- a/app/routes/first-sync.js +++ b/app/routes/first-sync.js @@ -1,9 +1,15 @@ import { later } from '@ember/runloop'; import config from 'travis/config/environment'; import SimpleLayoutRoute from 'travis/routes/simple-layout'; +import { inject as service } from '@ember/service'; +import { alias } from '@ember/object/computed'; export default SimpleLayoutRoute.extend({ + storage: service(), + accounts: service(), + user: alias('accounts.user'), + activate() { const controller = this.controllerFor('firstSync'); controller.addObserver('isSyncing', this, 'isSyncingDidChange'); @@ -15,11 +21,18 @@ export default SimpleLayoutRoute.extend({ return controller.removeObserver('controller.isSyncing', this, 'isSyncingDidChange'); }, + getTransition() { + if(this.user.hasV2Subscription || this.user.subscription) return 'account'; + if(this.storage.wizardStep < 2 && !this.user.colaborator) return 'account_activation'; + if(this.storage.wizardStep >=2 && this.storage.wizardStep <=3) return 'account/repositories'; + return 'account'; + }, + isSyncingDidChange() { const controller = this.controllerFor('firstSync'); if (!controller.isSyncing) { later( - () => this.transitionTo('account'), + () => this.transitionTo(this.getTransition()), config.timing.syncingPageRedirectionTime ); } diff --git a/app/services/auth.js b/app/services/auth.js index fc6ea6c4ce..99396d94e0 100644 --- a/app/services/auth.js +++ b/app/services/auth.js @@ -49,6 +49,7 @@ export default Service.extend({ metrics: service(), utm: service(), permissionsService: service('permissions'), + wizardStateService: service('wizard-state'), state: STATE.SIGNED_OUT, @@ -147,6 +148,18 @@ export default Service.extend({ this.signIn(provider); }, + signUp(provider) { + this.set('state', STATE.SIGNING_IN); + const url = new URL(this.redirectUrl || window.location.href); + + if (['/signin', '/plans', '/integration/bitbucket'].includes(url.pathname)) { + url.pathname = '/'; + } + const providerSegment = provider ? `/${provider}` : ''; + const path = `/auth/handshake${providerSegment}`; + window.location.href = `${authEndpoint || apiEndpoint}${path}?signup=true&redirect_uri=${url}`; + }, + signIn(provider) { this.set('state', STATE.SIGNING_IN); diff --git a/app/services/storage.js b/app/services/storage.js index a210a386c1..fe2aee0772 100644 --- a/app/services/storage.js +++ b/app/services/storage.js @@ -11,6 +11,25 @@ export default Service.extend({ this.setItem('travis.billing_step', +value); }, + get wizardStep() { + return +this.getItem('travis.wizard_step'); + }, + set wizardStep(value) { + this.setItem('travis.wizard_step', +value); + }, + + get selectedPlanId() { + console.log(this.getItem('travis.billing_selected_plan_id')); + return this.getItem('travis.billing_selected_plan_id'); + }, + set selectedPlanId(value) { + this.setItem('travis.billing_selected_plan_id', value); + }, + clearSelectedPlanId() { + this.removeItem('travis.billing_selected_plan_id'); + }, + + get billingInfo() { return this.parseWithDefault('travis.billing_info', {}); }, diff --git a/app/services/wizard-state.js b/app/services/wizard-state.js new file mode 100644 index 0000000000..4674d75e64 --- /dev/null +++ b/app/services/wizard-state.js @@ -0,0 +1,31 @@ +import Service, { inject as service } from '@ember/service'; +import { alias, reads } from '@ember/object/computed'; +import { task } from 'ember-concurrency'; +import { computed } from '@ember/object'; + +export default Service.extend({ + auth: service(), + api: service(), + storage: service(), + currentUser: alias('auth.currentUser'), + + currentState: reads('fetch.lastSuccessful.value'), + + state: computed('currentState', function() { + console.log("STATE!!"); + console.log(this.currentState.value); + return parseInt(this.currentState.value); + }), + + update: task(function* (val) { + return yield this.api.patch('/storage/billing_wizard_state', { data: {value: val}, travisApiVersion: '3' }); + }).drop(), + + delete: task(function* (val) { + return yield this.api.delete('/storage/billing_wizard_state', { travisApiVersion: '3' }); + }).drop(), + + fetch: task(function* () { + return yield this.api.get('/storage/billing_wizard_state', { travisApiVersion: '3' }); + }).drop() +}); diff --git a/app/styles/app.scss b/app/styles/app.scss index a672d3fce9..7c55ba2214 100644 --- a/app/styles/app.scss +++ b/app/styles/app.scss @@ -92,6 +92,7 @@ @import "app/layouts/insights"; @import "app/layouts/billing"; @import "app/layouts/scan-results"; +@import "app/layouts/activation"; @import "app/pages/landing"; @import "app/pages/home-pro"; diff --git a/app/styles/app/base.scss b/app/styles/app/base.scss index 22e1da3ec7..2be3de3e8d 100644 --- a/app/styles/app/base.scss +++ b/app/styles/app/base.scss @@ -202,6 +202,10 @@ span.emoji.emoji-sizer { line-height: 2; } +.float-left { + float: left; +} + .float-right { float: right; } @@ -310,4 +314,4 @@ span.emoji.emoji-sizer { .calendar-medium { @include ember-power-calendar($cell-size: 50px); -} \ No newline at end of file +} diff --git a/app/styles/app/layouts/activation.scss b/app/styles/app/layouts/activation.scss new file mode 100644 index 0000000000..2efe139235 --- /dev/null +++ b/app/styles/app/layouts/activation.scss @@ -0,0 +1,24 @@ +.layout-activation { + + @import "billing-plans"; + + .layout-activation-section { + width: 50%; + + .layout-activation-section__inner { + + width: 50%; + max-width: $max-width; + margin: 0 auto; + padding: $column-gutter/2; + } + + } + + .text-center { + position: relative; + top: 50%; + left: 50%; + } + +} diff --git a/app/styles/app/layouts/billing-plans.scss b/app/styles/app/layouts/billing-plans.scss new file mode 100644 index 0000000000..e589b1e778 --- /dev/null +++ b/app/styles/app/layouts/billing-plans.scss @@ -0,0 +1,383 @@ + .billing-plans { + background-color: #fff; + margin-bottom: 1em; + margin-top: 1em; + + &__addons_box { + background-color: #fff; + border-radius: 5px; + padding: 6px 13px; + border: 1px solid $pebble-grey; + margin-bottom: 10px; + width: 208px; + height: 114px; + flex-basis: auto; + margin-right: 5px; + + &--desc { + color: $cement-grey; + font-weight: 600; + font-size: 0.8em; + margin: 5px 0 12px 0; + text-transform: uppercase; + + span { + color: $cement-grey; + font-size: .8em; + font-style: italic; + } + } + + &--help { + &:hover { + border-bottom: 1px solid; + cursor: pointer; + } + } + + &--price { + margin: 0 0 10px 0; + font-weight: 300; + font-size: 1.7em; + text-align: left; + color: $oxide-blue; + + span { + color: $cement-grey; + font-size: .5em; + } + } + + &--name { + margin: 0px 0 10px 0; + color: $asphalt-grey; + font-size: 1em; + font-weight: 300; + text-align: left; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + &--button { + display: block; + max-width: 120px; + height: 36px; + margin: 25px auto 0 auto; + } + + &--cancel { + color: $asphalt-grey; + font-weight: 500; + font-size: 0.8em; + text-align: center; + margin: 40px 0 0 0; + } + + &--link { + cursor: pointer; + display: block; + max-width: fit-content; + max-width: -moz-fit-content; + margin: 20px auto 0 auto; + font-size: 0.7em; + text-align: center; + border-bottom: 1px solid transparent; + color: $oxide-blue; + + &:hover { + border-bottom-color: $oxide-blue; + } + } + + &--link-grey { + cursor: pointer; + display: inline-block; + border-bottom: 1px solid transparent; + color: $asphalt-grey; + margin: auto 1px; + border-bottom-color: $asphalt-grey; + } + + &:hover { + box-shadow: 0 0 5px 0px rgba(0, 0, 0, 0.15); + cursor: pointer; + } + } + + &__box-v2 { + background-color: #fff; + border-radius: 5px; + padding: 6px 13px; + border: 1px solid $pebble-grey; + margin-bottom: 10px; + width: 230px; + height: fit-content; + max-height: 550px; + flex-basis: auto; + margin-right: 5px; + + &--desc { + color: $asphalt-grey; + font-weight: 500; + font-size: 0.8em; + margin: 5px 0 12px 0; + + span { + color: $cement-grey; + font-size: .8em; + font-style: italic; + } + } + + &--desclink { + &:hover { + border-bottom: 1px solid; + cursor: pointer; + } + } + + &--help { + &:hover { + border-bottom: 1px solid; + cursor: pointer; + } + } + + &--price { + margin: 0 0 25px 0; + font-weight: 200; + font-size: 1.7em; + text-align: center; + color: $oxide-blue; + + span { + color: $cement-grey; + font-size: .5em; + } + } + + &--name { + margin: 8px 0 15px 0; + color: #000000; + font-size: 1.25em; + text-align: center; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + + &-bold { + @extend .billing-plans__box-v2--name; + font-weight: bolder; + } + } + + .billing-annual-plans{ + white-space: inherit; + } + + &--button { + display: block; + width: 122px; + height: 32px; + margin: 25px auto 0 auto; + border-radius: 5px; + } + + &--cancel { + color: $asphalt-grey; + font-weight: 500; + font-size: 0.8em; + text-align: center; + margin: 40px 0 0 0; + } + + &--link { + cursor: pointer; + display: block; + max-width: fit-content; + max-width: -moz-fit-content; + margin: 20px auto 0 auto; + font-size: 0.7em; + text-align: center; + border-bottom: 1px solid transparent; + color: $oxide-blue; + + &:hover { + border-bottom-color: $oxide-blue; + } + } + + &--link-grey { + cursor: pointer; + display: inline-block; + border-bottom: 1px solid transparent; + color: $asphalt-grey; + margin: auto 1px; + border-bottom-color: $asphalt-grey; + } + + &--button { + display: block; + max-width: 120px; + height: 36px; + margin: 25px auto 0 auto; + } + + &--cancel { + color: $asphalt-grey; + font-weight: 500; + font-size: 0.8em; + text-align: center; + margin: 40px 0 0 0; + } + + &--link { + cursor: pointer; + display: block; + max-width: fit-content; + margin: 20px auto 0 auto; + font-size: 0.7em; + text-align: center; + border-bottom: 1px solid transparent; + color: $oxide-blue; + + &:hover { + border-bottom-color: $oxide-blue; + } + } + + &--link-grey { + cursor: pointer; + display: inline-block; + border-bottom: 1px solid transparent; + color: $asphalt-grey; + margin: auto 1px; + border-bottom-color: $asphalt-grey; + } + + &:hover { + box-shadow: 0 0 5px 0px rgba(0, 0, 0, 0.15); + cursor: pointer; + } + } + + &__box-v2:last-child { + margin-right: 0; + } + + @media #{$medium-up} { + &__box { + flex-basis: 48%; + } + } + + @media #{$large-up} { + &__box { + flex-basis: 32%; + } + } + + @media #{$xlarge-up} { + &__box { + flex-basis: 24%; + } + } + + &__slider { + + margin-top: 2em; + + p { + margin-top: 1em; + text-transform: uppercase; + color: $cement-grey; + font-weight: 700; + font-size: .8em; + } + } + } + + .travis-modal { + .billing-select-plan { + h3 { + font-size: 48px; + font-weight: 300; + text-align: center; + } + } + } + + .billing-select-plan { + h4 { + margin: 35px 0 28px 0; + text-transform: uppercase; + text-align: center; + font-weight: 700; + color: $oxide-blue; + font-size: 12px; + } + + h3 { + margin-bottom: 0; + margin-top: 24px; + } + + h2 { + font-size: 16px; + font-weight: normal; + color: $asphalt-grey; + text-align: center; + } + + .switch { + .switch-inner { + width: 175px; + height: 30px; + margin-left: -5px; + padding-top: 1px; + + span { + width: 82px; + height: 25px; + line-height: 1.7; + padding-left: 0; + + span { + color: $ansi-black; + font-size: 1.2em; + font-weight: 500; + } + } + } + .on { + padding-left: 1px; + } + } + + .switch.active .switch-inner { + background-color: #d8d8d8; + } + + .highlight-plan { + color: $oxide-blue; + border: 1px solid $oxide-blue; + box-shadow: 0 0 5px 0px rgba(0, 0, 0, 0.15); + } + + .travistab-nav { + width: 530px; + margin-left: auto; + margin-right: auto; + + a { + cursor: pointer; + } + + li { + width: 250px; + font-size: 14px; + text-align: center; + } + } + } diff --git a/app/styles/app/layouts/billing.scss b/app/styles/app/layouts/billing.scss index d18a7ede22..072188469f 100644 --- a/app/styles/app/layouts/billing.scss +++ b/app/styles/app/layouts/billing.scss @@ -1,5 +1,7 @@ .billing { + @import 'billing-plans'; + .notice-banner--red { display: block; margin-top: 1rem; @@ -153,391 +155,7 @@ margin-left: 15px; } } - - .billing-plans { - background-color: #fff; - margin-bottom: 1em; - margin-top: 1em; - - &__addons_box { - background-color: #fff; - border-radius: 5px; - padding: 6px 13px; - border: 1px solid $pebble-grey; - margin-bottom: 10px; - width: 208px; - height: 114px; - flex-basis: auto; - margin-right: 5px; - - &--desc { - color: $cement-grey; - font-weight: 600; - font-size: 0.8em; - margin: 5px 0 12px 0; - text-transform: uppercase; - - span { - color: $cement-grey; - font-size: .8em; - font-style: italic; - } - } - - &--help { - &:hover { - border-bottom: 1px solid; - cursor: pointer; - } - } - - &--price { - margin: 0 0 10px 0; - font-weight: 300; - font-size: 1.7em; - text-align: left; - color: $oxide-blue; - - span { - color: $cement-grey; - font-size: .5em; - } - } - - &--name { - margin: 0px 0 10px 0; - color: $asphalt-grey; - font-size: 1em; - font-weight: 300; - text-align: left; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - - &--button { - display: block; - max-width: 120px; - height: 36px; - margin: 25px auto 0 auto; - } - - &--cancel { - color: $asphalt-grey; - font-weight: 500; - font-size: 0.8em; - text-align: center; - margin: 40px 0 0 0; - } - - &--link { - cursor: pointer; - display: block; - max-width: fit-content; - max-width: -moz-fit-content; - margin: 20px auto 0 auto; - font-size: 0.7em; - text-align: center; - border-bottom: 1px solid transparent; - color: $oxide-blue; - - &:hover { - border-bottom-color: $oxide-blue; - } - } - - &--link-grey { - cursor: pointer; - display: inline-block; - border-bottom: 1px solid transparent; - color: $asphalt-grey; - margin: auto 1px; - border-bottom-color: $asphalt-grey; - } - - &:hover { - box-shadow: 0 0 5px 0px rgba(0, 0, 0, 0.15); - cursor: pointer; - } - } - - &__box-v2 { - background-color: #fff; - border-radius: 5px; - padding: 6px 13px; - border: 1px solid $pebble-grey; - margin-bottom: 10px; - width: 230px; - height: fit-content; - max-height: 550px; - flex-basis: auto; - margin-right: 5px; - - &--desc { - color: $asphalt-grey; - font-weight: 500; - font-size: 0.8em; - margin: 5px 0 12px 0; - - span { - color: $cement-grey; - font-size: .8em; - font-style: italic; - } - } - - &--desclink { - &:hover { - border-bottom: 1px solid; - cursor: pointer; - } - } - - &--help { - &:hover { - border-bottom: 1px solid; - cursor: pointer; - } - } - - &--price { - margin: 0 0 25px 0; - font-weight: 200; - font-size: 1.7em; - text-align: center; - color: $oxide-blue; - - span { - color: $cement-grey; - font-size: .5em; - } - } - - &--name { - margin: 8px 0 15px 0; - color: #000000; - font-size: 1.25em; - text-align: center; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - - &-bold { - @extend .billing-plans__box-v2--name; - font-weight: bolder; - } - } - - .billing-annual-plans{ - white-space: inherit; - } - - &--button { - display: block; - width: 122px; - height: 32px; - margin: 25px auto 0 auto; - border-radius: 5px; - } - - &--cancel { - color: $asphalt-grey; - font-weight: 500; - font-size: 0.8em; - text-align: center; - margin: 40px 0 0 0; - } - - &--link { - cursor: pointer; - display: block; - max-width: fit-content; - max-width: -moz-fit-content; - margin: 20px auto 0 auto; - font-size: 0.7em; - text-align: center; - border-bottom: 1px solid transparent; - color: $oxide-blue; - - &:hover { - border-bottom-color: $oxide-blue; - } - } - - &--link-grey { - cursor: pointer; - display: inline-block; - border-bottom: 1px solid transparent; - color: $asphalt-grey; - margin: auto 1px; - border-bottom-color: $asphalt-grey; - } - - &--button { - display: block; - max-width: 120px; - height: 36px; - margin: 25px auto 0 auto; - } - - &--cancel { - color: $asphalt-grey; - font-weight: 500; - font-size: 0.8em; - text-align: center; - margin: 40px 0 0 0; - } - - &--link { - cursor: pointer; - display: block; - max-width: fit-content; - margin: 20px auto 0 auto; - font-size: 0.7em; - text-align: center; - border-bottom: 1px solid transparent; - color: $oxide-blue; - - &:hover { - border-bottom-color: $oxide-blue; - } - } - - &--link-grey { - cursor: pointer; - display: inline-block; - border-bottom: 1px solid transparent; - color: $asphalt-grey; - margin: auto 1px; - border-bottom-color: $asphalt-grey; - } - - &:hover { - box-shadow: 0 0 5px 0px rgba(0, 0, 0, 0.15); - cursor: pointer; - } - } - - &__box-v2:last-child { - margin-right: 0; - } - - @media #{$medium-up} { - &__box { - flex-basis: 48%; - } - } - - @media #{$large-up} { - &__box { - flex-basis: 32%; - } - } - - @media #{$xlarge-up} { - &__box { - flex-basis: 24%; - } - } - - &__slider { - - margin-top: 2em; - - p { - margin-top: 1em; - text-transform: uppercase; - color: $cement-grey; - font-weight: 700; - font-size: .8em; - } - } - } - - .travis-modal { - .billing-select-plan { - h3 { - font-size: 48px; - font-weight: 300; - text-align: center; - } - } - } - - .billing-select-plan { - h4 { - margin: 35px 0 28px 0; - text-transform: uppercase; - text-align: center; - font-weight: 700; - color: $oxide-blue; - font-size: 12px; - } - - h3 { - margin-bottom: 0; - margin-top: 24px; - } - - h2 { - font-size: 16px; - font-weight: normal; - color: $asphalt-grey; - text-align: center; - } - - .switch { - .switch-inner { - width: 175px; - height: 30px; - margin-left: -5px; - padding-top: 1px; - - span { - width: 82px; - height: 25px; - line-height: 1.7; - padding-left: 0; - - span { - color: $ansi-black; - font-size: 1.2em; - font-weight: 500; - } - } - } - .on { - padding-left: 1px; - } - } - - .switch.active .switch-inner { - background-color: #d8d8d8; - } - - .highlight-plan { - color: $oxide-blue; - border: 1px solid $oxide-blue; - box-shadow: 0 0 5px 0px rgba(0, 0, 0, 0.15); - } - - .travistab-nav { - width: 530px; - margin-left: auto; - margin-right: auto; - - a { - cursor: pointer; - } - - li { - width: 250px; - font-size: 14px; - text-align: center; - } - } - } - + .billing-italize { font-style: italic; } diff --git a/app/styles/app/layouts/top.scss b/app/styles/app/layouts/top.scss index 214d942d45..d3fe6c83ed 100644 --- a/app/styles/app/layouts/top.scss +++ b/app/styles/app/layouts/top.scss @@ -86,6 +86,11 @@ margin: auto; } + .layout-activation & { + max-width: $layout-max-width; + margin: auto; + } + @media #{$medium-up} { &, &.expanded { diff --git a/app/styles/app/modules/buttons.scss b/app/styles/app/modules/buttons.scss index 362e7a374b..7498075b4f 100644 --- a/app/styles/app/modules/buttons.scss +++ b/app/styles/app/modules/buttons.scss @@ -151,6 +151,21 @@ } } +.button--transparent { + @extend %button; + background: transparent; + + &.button--hover { + transition: all 0.1s linear; + + &:hover { + border-color: darken($pebble-grey, 10); + color: darken($asphalt-grey, 20); + background-color: lighten($pebble-grey, 2); + } + } +} + .button--green { @extend %button; diff --git a/app/styles/app/modules/forms.scss b/app/styles/app/modules/forms.scss index 150b47a519..da75812d13 100644 --- a/app/styles/app/modules/forms.scss +++ b/app/styles/app/modules/forms.scss @@ -24,6 +24,10 @@ textarea { @extend %input-base; } +.framed-form { + @extend %input-base; +} + ::placeholder { color: $cement-grey; } diff --git a/app/styles/app/modules/icons.scss b/app/styles/app/modules/icons.scss index d1002c967a..ad525fa77a 100644 --- a/app/styles/app/modules/icons.scss +++ b/app/styles/app/modules/icons.scss @@ -47,6 +47,14 @@ margin-left: 10px; } +.icon--sync { + @extend %icon; + + width: 30px; + height: 30px; + margin-right: 7px; +} + .icon svg, svg.icon, svg.icon--l, diff --git a/app/styles/app/modules/navigation.scss b/app/styles/app/modules/navigation.scss index 201dac0d37..b64f530ff6 100644 --- a/app/styles/app/modules/navigation.scss +++ b/app/styles/app/modules/navigation.scss @@ -99,6 +99,10 @@ $nav-line-height: 35px; } } +.disabled { + cursor: default; +} + .navigation-anchor { display: block; width: 100%; diff --git a/app/templates/account-activation.hbs b/app/templates/account-activation.hbs new file mode 100644 index 0000000000..a2ee596e7b --- /dev/null +++ b/app/templates/account-activation.hbs @@ -0,0 +1,21 @@ + + +

    Enable access to CI/CD world

    +
    + + + We don't like paperwork but to keep Travis CI free from spam and + + + cryptocurrency miners, we ask to verify your account with valid Credit Card. + + + Until you do this, you will not be able to use all the functionality of Travis CI. + +
    +
    + +
    +
    diff --git a/app/templates/components/billing/first-plan.hbs b/app/templates/components/billing/first-plan.hbs new file mode 100644 index 0000000000..8c540c235e --- /dev/null +++ b/app/templates/components/billing/first-plan.hbs @@ -0,0 +1,182 @@ +{{#if this.account.isFetchV2PlansRunning }} + +{{else}} + + + + + +
    + + + +
    + +
    + +
    + + + {{#if stripeError}} +

    {{stripeError.message}}

    + {{/if}} +
    +
    + +
    + +
    + + + + +
    +
    + + + +
    + +
    + + + +
    + +
    +
    + + + +
    +
    + + + +
    +
    + +
    + + + {{country}} + + +
    + {{#if this.isStateMandatory}} +
    + + + {{state}} + + +
    + {{/if}} + {{#if this.showNonZeroVatConfirmation}} +
    +
    + Is your company registered locally for VAT/GST? +
    +
    + + Yes + +
    +
    + + No + +
    +
    + {{/if}} + {{#if this.showVatField}} +
    + + + +
    + {{/if}} +
    +
    +
    +

    {{this.selectedPlan.name}}

    + {{#unless this.isTrial}} +

    {{format-currency this.selectedPlan.startingPrice floor="true"}}{{ if this.selectedPlan.isAnnual '/annualy' + '/monthly'}}

    + {{/unless}} +

    {{this.selectedPlan.name}}

    +
    +
    + +
    +
    +
    +
    + {{#if this.isTrial}} + + We will charge you $1 and refund you in 7 days. This is needed to make sure your card + + + is valid. by clicking on "Verify Your Account" you agree to Travis CI + Terms,Privacy + Policy. + + + + Your free Trial Plan ends on August 5,2022. If you cancel your free trial by that date you + + + will not be able to use Travis CI features. + + {{else}} + + You'll be charged {{format-currency this.selectedPlan.startingPrice floor="true"}} {{ if + this.selectedPlan.isAnnual 'annualy' 'monthly'}} until you cancel the subscription. Previous + + + charges won't be refunded when you cancel unless it's legally required. By clicking + + + + on {{this.getActivateButtonText}} you agree o Travis CI Terms,Privacy + Policy. + + + will not be able to use Travis CI features. + + {{/if}} +
    + +
    + {{#if this.isLoading}} + + {{else}} + + {{/if}} +
    + +
    + +
    +{{/if}} diff --git a/app/templates/components/header-links.hbs b/app/templates/components/header-links.hbs index 6ed4d1c121..31d28bc6de 100644 --- a/app/templates/components/header-links.hbs +++ b/app/templates/components/header-links.hbs @@ -50,9 +50,16 @@ {{#if this.features.proVersion}} {{#if this.auth.signedIn}}
  • + {{#unless this.isActivation}} Dashboard + {{else}} + + Dashboard + + + {{/unless}}
  • + {{yield}} +
  • diff --git a/app/templates/components/layouts/activation.hbs b/app/templates/components/layouts/activation.hbs new file mode 100644 index 0000000000..017da97118 --- /dev/null +++ b/app/templates/components/layouts/activation.hbs @@ -0,0 +1,7 @@ +
    + + +
    + {{yield (hash section=(component "layouts/activation-section"))}} +
    +
    diff --git a/app/templates/components/org-item.hbs b/app/templates/components/org-item.hbs index 8f57c52568..e7c369b510 100644 --- a/app/templates/components/org-item.hbs +++ b/app/templates/components/org-item.hbs @@ -12,4 +12,9 @@ {{this.name}} + {{#if this.showSync}} + + + + {{/if}} diff --git a/app/templates/components/owner/repositories.hbs b/app/templates/components/owner/repositories.hbs index e1b139136d..f8df7dcf69 100644 --- a/app/templates/components/owner/repositories.hbs +++ b/app/templates/components/owner/repositories.hbs @@ -1,3 +1,11 @@ + + + + + {{#if this.showMigrationStatusBanner}} {{#if this.owner.isMigrationBetaAccepted}} +
    + {{#if (eq this.wizardStep 2)}} +

    Add a .travis.yml file to your repository.

    +
    + + You need to add .travis.yml configuration file to root +
    + + directory of your repository. If a .travis.yml is not in your +
    + + repository or is not a valid YAML, Travis CI will ignore.it +
    +
    +

    Here you can find some of our basic language examples

    +
    + 1 of 2 +
    + + {{else}} +

    Trigger your next build

    +
    + + Once you've added .travis.yml to your repo, all you +
    + + need to do now is commit your changes and push +
    + + them to the repository +
    +
    +

    Want to learn more? Learn up on our docs

    +
    + 2 of 2 +
    +
    + + +
    + {{/if}} +
    +
    diff --git a/app/templates/components/profile-menu.hbs b/app/templates/components/profile-menu.hbs index aab5c6ebcc..2f4754d8cc 100644 --- a/app/templates/components/profile-menu.hbs +++ b/app/templates/components/profile-menu.hbs @@ -14,9 +14,13 @@ +
    {{/if}}
    -
    +

    {{this.selectedPlan.name}}

    {{#unless this.isTrial}}

    {{format-currency this.selectedPlan.startingPrice floor="true"}}{{ if this.selectedPlan.isAnnual '/annualy' '/monthly'}}

    {{/unless}} -

    {{this.selectedPlan.name}}

    + {{#if this.isTrial}} +

    Free plan valid one month

    + {{/if}}
    + {{#if this.planDetailsVisible}} +
    + {{#if (eq this.selectedPlan.planType 'hybrid')}} +

    + {{pluralize this.selectedPlan.concurrencyLimit "concurrent job"}} +

    + {{/if}} +

    + Private & Open-Source repos +

    +

    + Linux, Windows, macOS, FreeBSD +

    + + {{#if this.selectedPlan.hasOSSCreditAddons}} +

    + {{this.selectedPlan.publicCredits}} OSS Credits/month +

    + {{/if}} + {{#if this.selectedPlan.hasCreditAddons}} +

    + {{this.selectedPlan.privateCreditsTotal}} Credits +

    + {{/if}} + {{#if this.selectedPlan.hasOSSCreditAddons}} +

    + {{this.selectedPlan.publicCredits}} OSS Only Credits/month +

    + {{/if}} + {{#if this.selectedPlan.hasUserLicenseAddons}} +

    + {{#if this.selectedPlan.isUnlimitedUsers}} + Unlimited unique users + {{else}} + Up to {{this.selectedPlan.startingUsers}} unique users +

    + Charged monthly per usage - check pricing +
    + {{/if}} +

    + {{/if}} +
    + {{/if}} {{/unless}} - {{#if this.isTrial}} + {{#if this.isTrial}}

    Free plan valid one month

    {{/if}}
    -
    -
    -
    -
    +
    {{#if this.isTrial}} - We will charge you $1 and refund you in 7 days. This is needed to make sure your card - - - is valid. by clicking on "Verify Your Account" you agree to Travis CI - Terms,Privacy + + is valid. by clicking on "Verify Your Account" you agree to Travis CI + Terms,Privacy Policy. - - Your free Trial Plan ends on August 5,2022. If you cancel your free trial by that date you - - + will not be able to use Travis CI features. - {{else}} - + You'll be charged {{format-currency this.selectedPlan.startingPrice floor="true"}} {{ if this.selectedPlan.isAnnual 'annualy' 'monthly'}} until you cancel the subscription. Previous - - + charges won't be refunded when you cancel unless it's legally required. By clicking - - - - on {{this.getActivateButtonText}} you agree o Travis CI Terms,Privacy + on {{this.getActivateButtonText}} you agree o Travis CI Terms,Privacy Policy. - - - will not be able to use Travis CI features. - {{/if}}
    -
    +
    {{#if this.isLoading}} {{else}} @@ -182,4 +209,4 @@
    -{{/if}} +{{/if}} \ No newline at end of file diff --git a/app/templates/components/forms/form-field.hbs b/app/templates/components/forms/form-field.hbs index 52afe0f93a..c7f69a6567 100644 --- a/app/templates/components/forms/form-field.hbs +++ b/app/templates/components/forms/form-field.hbs @@ -94,6 +94,14 @@ validateMultipleInputs=(action "validateMultipleInputs") onInit=(action "setFieldElementId") ) + placeholder=(component "forms/form-placeholder" + form=this.form + onFocus=(action "handleFocus") + onBlur=(action "handleBlur") + onChange=(action "handleChange") + onInit=(action "setFieldElementId") + class="travis-form__field-component" + ) ) }} diff --git a/app/templates/components/forms/form-placeholder.hbs b/app/templates/components/forms/form-placeholder.hbs new file mode 100644 index 0000000000..f0d6be6ba5 --- /dev/null +++ b/app/templates/components/forms/form-placeholder.hbs @@ -0,0 +1,3 @@ +
    +{{yield}} +
    diff --git a/app/templates/components/header-links.hbs b/app/templates/components/header-links.hbs index 31d28bc6de..eff2df5366 100644 --- a/app/templates/components/header-links.hbs +++ b/app/templates/components/header-links.hbs @@ -55,10 +55,9 @@ Dashboard {{else}} - + Dashboard - - + {{/unless}}
  • diff --git a/app/templates/components/org-item.hbs b/app/templates/components/org-item.hbs index e7c369b510..29ee2d52b1 100644 --- a/app/templates/components/org-item.hbs +++ b/app/templates/components/org-item.hbs @@ -14,7 +14,7 @@ {{#if this.showSync}} - + {{/if}} diff --git a/app/templates/components/owner/repositories.hbs b/app/templates/components/owner/repositories.hbs index f8df7dcf69..ac43d3d8e5 100644 --- a/app/templates/components/owner/repositories.hbs +++ b/app/templates/components/owner/repositories.hbs @@ -1,10 +1,4 @@ - - - {{#if this.showMigrationStatusBanner}} {{#if this.owner.isMigrationBetaAccepted}} diff --git a/app/templates/components/owner/wizard.hbs b/app/templates/components/owner/wizard.hbs index d2592f479d..f691dec7f6 100644 --- a/app/templates/components/owner/wizard.hbs +++ b/app/templates/components/owner/wizard.hbs @@ -1,8 +1,8 @@ -
    +
    {{#if (eq this.wizardStep 2)}} -

    Add a .travis.yml file to your repository.

    -
    +

    Add a .travis.yml file to your repository.

    +
    You need to add .travis.yml configuration file to root
    @@ -13,16 +13,16 @@ repository or is not a valid YAML, Travis CI will ignore.it
    -

    Here you can find some of our basic language examples

    +

    Here you can find some of our basic language examples

    1 of 2
    - {{else}} -

    Trigger your next build

    -
    +

    Trigger your next build

    +
    Once you've added .travis.yml to your repo, all you
    @@ -33,11 +33,11 @@ them to the repository
    -

    Want to learn more? Learn up on our docs

    +

    Want to learn more? Learn up on our docs

    2 of 2
    -
    +
    diff --git a/app/templates/components/profile-menu.hbs b/app/templates/components/profile-menu.hbs index 2f4754d8cc..287668d9db 100644 --- a/app/templates/components/profile-menu.hbs +++ b/app/templates/components/profile-menu.hbs @@ -18,7 +18,7 @@ {{on 'click' (action 'toggleMenu')}} > {{#if this.isActivation}} - + {{/if}}
    • - +
    @@ -105,6 +105,13 @@ Repositories + + + +
  • diff --git a/app/templates/components/sync-button.hbs b/app/templates/components/sync-button.hbs index df5abe287f..3e840989a1 100644 --- a/app/templates/components/sync-button.hbs +++ b/app/templates/components/sync-button.hbs @@ -15,10 +15,13 @@ data-test-start-sync-button class="button--transparent" type="button" + disabled={{if this.isSyncDisabled true false}} onclick={{action "sync"}} > - - + + +

    Last synced {{format-time this.user.syncedAt}}

    +
    diff --git a/app/templates/components/top-bar.hbs b/app/templates/components/top-bar.hbs index 8ac6480a58..428c38c757 100644 --- a/app/templates/components/top-bar.hbs +++ b/app/templates/components/top-bar.hbs @@ -8,7 +8,7 @@ - +
    {{#if this.isUnconfirmed}} diff --git a/app/templates/components/ui-kit/link.hbs b/app/templates/components/ui-kit/link.hbs index 1c268636a1..e1ed0c488d 100644 --- a/app/templates/components/ui-kit/link.hbs +++ b/app/templates/components/ui-kit/link.hbs @@ -17,6 +17,7 @@ {{~else~}} + + + + + + + + + + From 6ae496166b81ae6edf99bf234e432b19047235d7 Mon Sep 17 00:00:00 2001 From: AndriiMysko Date: Mon, 13 Mar 2023 17:35:57 +0200 Subject: [PATCH 13/43] sync subscriptions on first_syn --- app/models/user.js | 4 ++++ app/routes/first-sync.js | 14 +++++++++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/app/models/user.js b/app/models/user.js index 3f56b9dcf8..9dcf2473af 100644 --- a/app/models/user.js +++ b/app/models/user.js @@ -97,6 +97,10 @@ export default Owner.extend({ this.permissionsService.fetchPermissions.perform(); this.wizardStateService.fetch.perform(); this.accounts.fetchOrganizations.perform(); + this.accounts.fetchSubscriptions.perform(); + this.accounts.fetchV2Subscriptions.perform(); + + console.log("SYNC2"); this.applyReposFilter(); Travis.trigger('user:synced', this); this.set('isSyncing', false); diff --git a/app/routes/first-sync.js b/app/routes/first-sync.js index afae3ca26d..504ccbc05a 100644 --- a/app/routes/first-sync.js +++ b/app/routes/first-sync.js @@ -22,7 +22,7 @@ export default SimpleLayoutRoute.extend({ }, getTransition() { - if(this.user.hasV2Subscription || this.user.subscription) return 'account'; + if(this.user.hasV2Subscription || this.user.subscription || this.user.accountSubscriptions.length > 0 || this.user.accountv2Subscriptions.length > 0) return 'account'; if(this.storage.wizardStep < 2 && !this.user.colaborator) return 'account_activation'; if(this.storage.wizardStep >=2 && this.storage.wizardStep <=3) return 'account/repositories'; return 'account'; @@ -31,10 +31,14 @@ export default SimpleLayoutRoute.extend({ isSyncingDidChange() { const controller = this.controllerFor('firstSync'); if (!controller.isSyncing) { - later( - () => this.transitionTo(this.getTransition()), - config.timing.syncingPageRedirectionTime - ); + this.accounts.fetchSubscriptions.perform() + .then(() => this.accounts.fetchV2Subscriptions.perform()) + .then(() => { + later( + () => this.transitionTo(this.getTransition()), + config.timing.syncingPageRedirectionTime + ); + }); } } From 21cd028650a59de195215f41b044b099c862ee77 Mon Sep 17 00:00:00 2001 From: AndriiMysko Date: Wed, 15 Mar 2023 09:30:50 +0200 Subject: [PATCH 14/43] redirect from ghapp installation to firstsync --- app/components/billing/first-plan.js | 10 +++++----- app/controllers/github-apps-installation.js | 3 ++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/app/components/billing/first-plan.js b/app/components/billing/first-plan.js index fe5b190d33..ccefb81886 100644 --- a/app/components/billing/first-plan.js +++ b/app/components/billing/first-plan.js @@ -149,12 +149,11 @@ export default Component.extend({ const { token } = yield this.stripe.createStripeToken.perform(stripeElement); if (token) { - const organizationId = null;// account.type === 'organization' ? +(account.id) : null; + const plan = selectedPlan && selectedPlan.id && this.store.peekRecord('v2-plan-config', selectedPlan.id); - const org = organizationId && this.store.peekRecord('organization', organizationId); this.subscription.setProperties({ - organization: org, + organization: null, plan: plan, v1SubscriptionId: this.v1SubscriptionId, }); @@ -184,8 +183,9 @@ export default Component.extend({ this.storage.clearSelectedPlanId(); this.storage.wizardStep = 2; this.wizard.update.perform(2); - this.router.transitionTo('/account/repositories'); - + yield this.accounts.fetchV2Subscriptions.perform().then(() => { + this.router.transitionTo('/account/repositories'); + }); } this.flashes.success("Your account has been successfully activated"); } catch (error) { diff --git a/app/controllers/github-apps-installation.js b/app/controllers/github-apps-installation.js index f470d3088f..3f48dfd5ea 100644 --- a/app/controllers/github-apps-installation.js +++ b/app/controllers/github-apps-installation.js @@ -19,7 +19,8 @@ export default Controller.extend({ startPolling() { this.initialDelayPromise().then(() => this.fetchPromise().then(() => { - this.transitionToRoute('account'); + + this.transitionToRoute('first_sync'); })); }, From 4d428737a366b8f038835dea4bd236ddc253eaaa Mon Sep 17 00:00:00 2001 From: gbarc80 Date: Thu, 16 Mar 2023 12:30:01 +0100 Subject: [PATCH 15/43] style fixes,profile menu update,fix for company --- app/components/billing/first-plan.js | 70 +++++++++---------- app/components/owner/repositories.js | 2 - app/components/owner/wizard.js | 6 +- app/components/profile-menu.js | 1 - app/components/sync-button.js | 2 +- app/components/top-bar.js | 2 +- app/components/ui-kit/button-signin.js | 5 +- app/controllers/application.js | 2 +- app/controllers/github-apps-installation.js | 1 - app/models/user.js | 1 - app/routes/account-activation.js | 3 - app/routes/application.js | 10 +-- app/routes/basic.js | 2 +- app/routes/first-sync.js | 9 ++- app/services/storage.js | 1 - app/services/wizard-state.js | 8 +-- app/styles/app/layouts/activation.scss | 23 ++++++ .../components/billing/first-plan.hbs | 6 +- app/templates/components/profile-menu.hbs | 10 ++- 19 files changed, 83 insertions(+), 81 deletions(-) diff --git a/app/components/billing/first-plan.js b/app/components/billing/first-plan.js index ccefb81886..908ec4a98b 100644 --- a/app/components/billing/first-plan.js +++ b/app/components/billing/first-plan.js @@ -1,11 +1,10 @@ import Component from '@ember/component'; import { task } from 'ember-concurrency'; import { inject as service } from '@ember/service'; -import { or, not, reads, filterBy, alias } from '@ember/object/computed'; +import { not, reads, filterBy, alias } from '@ember/object/computed'; import { computed } from '@ember/object'; -import { typeOf } from '@ember/utils'; import config from 'travis/config/environment'; -import { countries,states,zeroVatThresholdCountries, nonZeroVatThresholdCountries, stateCountries } from 'travis/utils/countries'; +import { countries, states, zeroVatThresholdCountries, nonZeroVatThresholdCountries, stateCountries } from 'travis/utils/countries'; export default Component.extend({ stripe: service(), @@ -18,12 +17,7 @@ export default Component.extend({ router: service(), wizard: service('wizard-state'), countries, - states: computed('country', function() { - const { country } = this; - return states[country]; - }), - - user: null, + user: null, account: alias('accounts.user'), stripeElement: null, stripeLoading: false, @@ -35,8 +29,8 @@ export default Component.extend({ defaultPlans: filterBy('availablePlans', 'trialPlan'), defaultPlanId: reads('defaultPlans.firstObject.id'), showCancelButton: false, - travisTermsUrl: "/terms", - travisPolicyUrl: "/policy", + travisTermsUrl: '/terms', + travisPolicyUrl: '/policy', subscription: null, vatId: null, @@ -57,13 +51,13 @@ export default Component.extend({ }), hasLocalRegistration: false, - firstName: "", - lastName: "", - company: "", - address: "", - city: "", - country: "", - billingEmail: "", + firstName: '', + lastName: '', + company: '', + address: '', + city: '', + country: '', + billingEmail: '', billingEmails: computed('billingEmail', function () { return (this.billingEmail || '').split(','); }), @@ -116,44 +110,40 @@ export default Component.extend({ return !this.creditCardInfo.lastDigits; }), - getPriceInfo: computed('selectedPlan', function() { + getPriceInfo: computed('selectedPlan', function () { let plan = this.selectedPlan; - console.log(plan); - return '$' + plan.startingPrice + (plan.isAnnual ? ' annualy' :' monthly'); + return `$${plan.startingPrice} ${(plan.isAnnual ? ' annualy' : ' monthly')}`; }), - getActivateButtonText: computed('selectedPlan', function() { - let text = "Verify Your Account"; + getActivateButtonText: computed('selectedPlan', function () { + let text = 'Verify Your Account'; let plan = this.selectedPlan; if (plan && !plan.isTrial) { - text = "Activate " + plan.name; + text = `Activate ${plan.name}`; } return text; }), - canActivate: computed('country','zipCode','address','creditCardOwner','city', 'stripeElement', 'billingEmail', function() { + canActivate: computed('country', 'zipCode', 'address', 'creditCardOwner', 'city', 'stripeElement', 'billingEmail', function () { return this.billingEmail && this.country && this.zipCode && this.address && this.creditCardOwner && this.stripeElement && this.city; }), - - createSubscription: task(function* () { this.metrics.trackEvent({ action: 'Pay Button Clicked', category: 'Subscription', }); - const { stripeElement, account, selectedPlan } = this; + const { stripeElement, selectedPlan } = this; try { - this.set('subscription',this.newV2Subscription()); - + this.set('subscription', this.newV2Subscription()); const { token } = yield this.stripe.createStripeToken.perform(stripeElement); - if (token) { - + const organizationId = null; const plan = selectedPlan && selectedPlan.id && this.store.peekRecord('v2-plan-config', selectedPlan.id); + const org = organizationId && this.store.peekRecord('organization', organizationId); this.subscription.setProperties({ - organization: null, + organization: org, plan: plan, v1SubscriptionId: this.v1SubscriptionId, }); @@ -187,7 +177,7 @@ export default Component.extend({ this.router.transitionTo('/account/repositories'); }); } - this.flashes.success("Your account has been successfully activated"); + this.flashes.success('Your account has been successfully activated'); } catch (error) { this.handleError(); } @@ -197,11 +187,21 @@ export default Component.extend({ const plan = this.store.createRecord('v2-plan-config'); const billingInfo = this.store.createRecord('v2-billing-info'); const creditCardInfo = this.store.createRecord('v2-credit-card-info'); + let ownerName = this.creditCardOwner.trim(); + let idx = ownerName.lastIndexOf(' '); + if (idx > 0) { + this.firstName = ownerName.substr(0, idx); + this.lastName = ownerName.substr(idx + 1); + } else { + this.firstName = ''; + this.lastName = ownerName; + } billingInfo.setProperties({ firstName: this.firstName, lastName: this.lastName, address: this.address, city: this.city, + company: this.company, zipCode: this.zipCode, country: this.country, state: this.state, @@ -251,7 +251,7 @@ export default Component.extend({ this.hasLocalRegistration = false; }, togglePlanDetails() { - this.set('planDetailsVisible', !this.planDetailsVisible); + this.set('planDetailsVisible', !this.planDetailsVisible); } } diff --git a/app/components/owner/repositories.js b/app/components/owner/repositories.js index e866b2d9f2..ee0e021b6d 100644 --- a/app/components/owner/repositories.js +++ b/app/components/owner/repositories.js @@ -83,8 +83,6 @@ export default Component.extend({ wizardState: reads('wizard.state'), showWizard: computed('wizardStep', function () { - console.log(`STEP:`); - console.log( this.wizardStep); let state = this.wizardStep; return state && state <= 3; diff --git a/app/components/owner/wizard.js b/app/components/owner/wizard.js index 5820f4f22d..85064117dd 100644 --- a/app/components/owner/wizard.js +++ b/app/components/owner/wizard.js @@ -2,8 +2,6 @@ import Component from '@ember/component'; import { inject as service } from '@ember/service'; import { task } from 'ember-concurrency'; import { computed } from '@ember/object'; -import { later } from '@ember/runloop'; -import { or, reads, filterBy } from '@ember/object/computed'; import config from 'travis/config/environment'; const { docs, languages } = config.urls; @@ -21,20 +19,18 @@ export default Component.extend({ onClose: null, travisDocsUrl: computed(() => `${docs}`), travisBasicLanguageExamplesUrl: computed(() => `${languages}`), - showWizard: false, updateStep: task(function* (val) { let step = parseInt(this.wizardStep) + val; this.set('wizardStep', step); this.storage.wizardStep = step; yield this.wizard.update.perform(step); - }).drop(), actions: { nextStep() { this.updateStep.perform(1); - if (this.wizardStep > 3) this.get('onClose') (); + if (this.wizardStep > 3) this.get('onClose')(); }, previousStep() { this.updateStep.perform(-1); diff --git a/app/components/profile-menu.js b/app/components/profile-menu.js index 123e3c1ed5..9e7967b8dd 100644 --- a/app/components/profile-menu.js +++ b/app/components/profile-menu.js @@ -75,7 +75,6 @@ export default Component.extend({ }, toggleMenu() { - if(this.isActivation) return this.closeMenu(); if (this.isMenuOpen) { this.closeMenu(); } else { diff --git a/app/components/sync-button.js b/app/components/sync-button.js index 9b9956378a..7681f0cbf2 100644 --- a/app/components/sync-button.js +++ b/app/components/sync-button.js @@ -22,7 +22,7 @@ export default Component.extend({ return this.user.sync(this.isOrganization); }, updateState() { - this.isSyncDisabled(); + this.isSyncDisabled(); } } }); diff --git a/app/components/top-bar.js b/app/components/top-bar.js index 7952d16642..f206d06610 100644 --- a/app/components/top-bar.js +++ b/app/components/top-bar.js @@ -29,7 +29,7 @@ export default Component.extend(InViewportMixin, { user: reads('auth.currentUser'), isUnconfirmed: computed('user.confirmedAt', function () { - if (!this.user || (this.storage.wizardStep > 0 && this.storage.wizardStep <=3)) + if (!this.user || (this.storage.wizardStep > 0 && this.storage.wizardStep <= 1)) return false; return !this.user.confirmedAt; }), diff --git a/app/components/ui-kit/button-signin.js b/app/components/ui-kit/button-signin.js index fa131d1464..560c20a02e 100644 --- a/app/components/ui-kit/button-signin.js +++ b/app/components/ui-kit/button-signin.js @@ -54,10 +54,9 @@ export default Component.extend({ this.auth.switchAccount(this.account.id, this.auth.redirectUrl || '/'); } else { this.set('isLoading', true); - if(this.isSignup) { + if (this.isSignup) { this.auth.signUp(this.provider); - } - else { + } else { this.auth.signInWith(this.provider); } } diff --git a/app/controllers/application.js b/app/controllers/application.js index 171763c9aa..14597527de 100644 --- a/app/controllers/application.js +++ b/app/controllers/application.js @@ -48,7 +48,7 @@ export default Controller.extend({ this._super(...arguments); this.router.on('routeDidChange', () => this.handleRouteChange()); this.router.on('routeWillChange', (transition) => { - if(this.selectedPlanId) { + if (this.selectedPlanId) { this.storage.selectedPlanId = this.selectedPlanId; } }); diff --git a/app/controllers/github-apps-installation.js b/app/controllers/github-apps-installation.js index 3f48dfd5ea..dd1d540e19 100644 --- a/app/controllers/github-apps-installation.js +++ b/app/controllers/github-apps-installation.js @@ -19,7 +19,6 @@ export default Controller.extend({ startPolling() { this.initialDelayPromise().then(() => this.fetchPromise().then(() => { - this.transitionToRoute('first_sync'); })); }, diff --git a/app/models/user.js b/app/models/user.js index 9dcf2473af..115e96b047 100644 --- a/app/models/user.js +++ b/app/models/user.js @@ -100,7 +100,6 @@ export default Owner.extend({ this.accounts.fetchSubscriptions.perform(); this.accounts.fetchV2Subscriptions.perform(); - console.log("SYNC2"); this.applyReposFilter(); Travis.trigger('user:synced', this); this.set('isSyncing', false); diff --git a/app/routes/account-activation.js b/app/routes/account-activation.js index cbfc5d7955..a0b11cdd34 100644 --- a/app/routes/account-activation.js +++ b/app/routes/account-activation.js @@ -1,4 +1,3 @@ -import Route from '@ember/routing/route'; import { inject as service } from '@ember/service'; import SimpleLayoutRoute from 'travis/routes/simple-layout'; @@ -12,13 +11,11 @@ export default SimpleLayoutRoute.extend({ wizardStateService: service('wizard-state'), activate() { - console.log("activate"); this.storage.wizardStep = 1; this.wizardStateService.update.perform(1); }, deactivate() { - console.log("deactivate"); let step = this.storage.wizardStep; if (step == 2 || step == 3) this.transitionTo('/account/repositories'); }, diff --git a/app/routes/application.js b/app/routes/application.js index 0c616b87dd..32f906b9aa 100644 --- a/app/routes/application.js +++ b/app/routes/application.js @@ -16,7 +16,7 @@ export default TravisRoute.extend(BuildFaviconMixin, { flashes: service(), repositories: service(), storage: service(), - wizard: service('wizard-state'), + wizard: service('wizard-state'), queryParams: { selectedPlanId: null, }, @@ -37,16 +37,12 @@ export default TravisRoute.extend(BuildFaviconMixin, { return this.auth.autoSignIn(); }, - afterModel() { - console.log("APP AFTER CALLED"); - }, - model(model, transition) { - if(model.selectedPlanId) { + if (model.selectedPlanId) { this.storage.selectedPlanId = model.selectedPlanId; } if (this.auth.signedIn) { - this.wizard.fetch.perform().then(() => {this.storage.wizardStep = this.wizard.state;}); + this.wizard.fetch.perform().then(() => { this.storage.wizardStep = this.wizard.state; }); return this.get('featureFlags.fetchTask').perform(); } }, diff --git a/app/routes/basic.js b/app/routes/basic.js index 79d82eb2aa..3eac8d5b2f 100644 --- a/app/routes/basic.js +++ b/app/routes/basic.js @@ -8,7 +8,7 @@ export default Route.extend({ storage: service(), activate() { - if(this.storage.wizardStep > 0 && this.storage.wizardStep <=3) { + if (this.storage.wizardStep > 0 && this.storage.wizardStep <= 3) { if (this.storage.wizardStep == 1) { this.transitionTo('account_activation'); } else { diff --git a/app/routes/first-sync.js b/app/routes/first-sync.js index 504ccbc05a..0f89d5d018 100644 --- a/app/routes/first-sync.js +++ b/app/routes/first-sync.js @@ -22,9 +22,12 @@ export default SimpleLayoutRoute.extend({ }, getTransition() { - if(this.user.hasV2Subscription || this.user.subscription || this.user.accountSubscriptions.length > 0 || this.user.accountv2Subscriptions.length > 0) return 'account'; - if(this.storage.wizardStep < 2 && !this.user.colaborator) return 'account_activation'; - if(this.storage.wizardStep >=2 && this.storage.wizardStep <=3) return 'account/repositories'; + if (this.user.hasV2Subscription || + this.user.subscription || + this.user.accountSubscriptions.length > 0 || + this.user.accountv2Subscriptions.length > 0) return 'account'; + if (this.storage.wizardStep < 2 && !this.user.colaborator) return 'account_activation'; + if (this.storage.wizardStep >= 2 && this.storage.wizardStep <= 3) return 'account/repositories'; return 'account'; }, diff --git a/app/services/storage.js b/app/services/storage.js index fe2aee0772..04921811c1 100644 --- a/app/services/storage.js +++ b/app/services/storage.js @@ -19,7 +19,6 @@ export default Service.extend({ }, get selectedPlanId() { - console.log(this.getItem('travis.billing_selected_plan_id')); return this.getItem('travis.billing_selected_plan_id'); }, set selectedPlanId(value) { diff --git a/app/services/wizard-state.js b/app/services/wizard-state.js index b92ebf2986..bd2bb904fa 100644 --- a/app/services/wizard-state.js +++ b/app/services/wizard-state.js @@ -11,15 +11,13 @@ export default Service.extend({ currentState: reads('fetch.lastSuccessful.value'), - state: computed('currentState', function() { - console.log("STATE!!"); - console.log(this.currentState.value); + state: computed('currentState', function () { return parseInt(this.currentState.value); }), - isEnabled: computed('currentState', function() { + isEnabled: computed('currentState', function () { let val = parseInt(this.currentState.value); - return val>=1 && val<=3; + return val >= 1 && val <= 3; }), update: task(function* (val) { diff --git a/app/styles/app/layouts/activation.scss b/app/styles/app/layouts/activation.scss index a67740c4ac..95a04c90ec 100644 --- a/app/styles/app/layouts/activation.scss +++ b/app/styles/app/layouts/activation.scss @@ -132,4 +132,27 @@ .travis-link { color: #3EAAAF; } + + .StripeElement { + display: block; + font-size: 1em; + border-radius: 8px; + border: 4px solid transparent; + margin: -4px; + padding: 10px 9px; + box-shadow: inset 0 0 1px 1px #d0d0d0; + } + + .StripeElement--focus { + outline: none; + border-color: rgba(62, 170, 175, 0.2); + } + + .StripeElement--invalid { + box-shadow: inset 0 0 1px 1px red; + } + + .StripeElement--webkit-autofill { + background-color: #FEFDE5 !important; + } } diff --git a/app/templates/components/billing/first-plan.hbs b/app/templates/components/billing/first-plan.hbs index 116157d0ef..dd124fbd28 100644 --- a/app/templates/components/billing/first-plan.hbs +++ b/app/templates/components/billing/first-plan.hbs @@ -18,13 +18,11 @@
    - {{#if stripeError}}

    {{stripeError.message}}

    {{/if}}
    -
    @@ -37,7 +35,7 @@
  • - +
    @@ -209,4 +207,4 @@
    -{{/if}} \ No newline at end of file +{{/if}} diff --git a/app/templates/components/profile-menu.hbs b/app/templates/components/profile-menu.hbs index 287668d9db..c429bcb1b9 100644 --- a/app/templates/components/profile-menu.hbs +++ b/app/templates/components/profile-menu.hbs @@ -14,12 +14,10 @@

    {{#if (and this.subscription.isStripe this.showPlanInfo)}} diff --git a/app/templates/components/header-links.hbs b/app/templates/components/header-links.hbs index eff2df5366..65f352fe63 100644 --- a/app/templates/components/header-links.hbs +++ b/app/templates/components/header-links.hbs @@ -55,7 +55,7 @@ Dashboard {{else}} - + Dashboard {{/unless}} diff --git a/app/templates/components/owner/wizard.hbs b/app/templates/components/owner/wizard.hbs index f691dec7f6..79270c7b0a 100644 --- a/app/templates/components/owner/wizard.hbs +++ b/app/templates/components/owner/wizard.hbs @@ -14,7 +14,7 @@

    Here you can find some of our basic language examples

    -
    +
    1 of 2
    -

    Want to learn more? Learn up on our docs

    -
    +
    2 of 2
    -
    - -
    diff --git a/app/templates/components/plan_usage.hbs b/app/templates/components/plan_usage.hbs index fbfc0be34d..dc475cc668 100644 --- a/app/templates/components/plan_usage.hbs +++ b/app/templates/components/plan_usage.hbs @@ -29,17 +29,8 @@ {{/each}} - {{#if this.v2subscription.hasUserLicenseAddons}} - - - {{/if}}
    -
    - -

    Usage statistics

    diff --git a/app/templates/components/profile-nav.hbs b/app/templates/components/profile-nav.hbs index 3d65ef3941..2e23e484db 100644 --- a/app/templates/components/profile-nav.hbs +++ b/app/templates/components/profile-nav.hbs @@ -98,7 +98,7 @@ @showLink={{this.model.subscriptionPermissions.create}} /> {{/if}} -
    From 028299d0d699e7fe24ff56db50655c33c113d243 Mon Sep 17 00:00:00 2001 From: gbarc80 Date: Tue, 21 Mar 2023 11:36:28 +0100 Subject: [PATCH 19/43] redirection updates, typos fixed --- app/components/billing/first-plan.js | 13 ++++++++++++- app/routes/account-activation.js | 9 ++++++--- app/routes/first-sync.js | 6 ++++-- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/app/components/billing/first-plan.js b/app/components/billing/first-plan.js index 700194dd1b..9bef7d8929 100644 --- a/app/components/billing/first-plan.js +++ b/app/components/billing/first-plan.js @@ -179,7 +179,18 @@ export default Component.extend({ } this.flashes.success('Your account has been successfully activated'); } catch (error) { - this.handleError(); + yield this.accounts.fetchV2Subscriptions.perform().then(() => { + if( this.accounts.user.subscription || this.accounts.user.v2subscription) { + this.storage.clearBillingData(); + this.storage.clearSelectedPlanId(); + this.storage.wizardStep = 2; + this.wizard.update.perform(2); + this.router.transitionTo('account.repositories'); + } + else { + this.handleError(); + } + }); } }).drop(), diff --git a/app/routes/account-activation.js b/app/routes/account-activation.js index a0b11cdd34..42d902a00a 100644 --- a/app/routes/account-activation.js +++ b/app/routes/account-activation.js @@ -8,16 +8,19 @@ export default SimpleLayoutRoute.extend({ features: service(), stripe: service(), storage: service(), + store: service(), wizardStateService: service('wizard-state'), activate() { - this.storage.wizardStep = 1; - this.wizardStateService.update.perform(1); + if( this.storage.wizardStep < 1) { + this.storage.wizardStep = 1; + this.wizardStateService.update.perform(1); + } }, deactivate() { let step = this.storage.wizardStep; - if (step == 2 || step == 3) this.transitionTo('/account/repositories'); + // if (step == 2 || step == 3) this.transitionTo('/account/repositories'); }, title: 'Travis CI - Select Plan', diff --git a/app/routes/first-sync.js b/app/routes/first-sync.js index 0f89d5d018..913c492c99 100644 --- a/app/routes/first-sync.js +++ b/app/routes/first-sync.js @@ -22,11 +22,13 @@ export default SimpleLayoutRoute.extend({ }, getTransition() { - if (this.user.hasV2Subscription || + if (this.user.vcsType == 'AssemblaUser') return 'account'; + if (this.user.collaborator || + this.user.hasV2Subscription || this.user.subscription || this.user.accountSubscriptions.length > 0 || this.user.accountv2Subscriptions.length > 0) return 'account'; - if (this.storage.wizardStep < 2 && !this.user.colaborator) return 'account_activation'; + if (this.storage.wizardStep < 2 && !this.user.collaborator) return 'account_activation'; if (this.storage.wizardStep >= 2 && this.storage.wizardStep <= 3) return 'account/repositories'; return 'account'; }, From 4fa677a576cce29a0413a8e56f23445a77ab375d Mon Sep 17 00:00:00 2001 From: gbarc80 Date: Thu, 23 Mar 2023 11:07:59 +0100 Subject: [PATCH 20/43] ui updates - plan, wizard, activation --- app/components/billing/first-plan.js | 5 +- app/components/forms/form-radio.js | 47 +++++++ app/components/org-item.js | 3 +- app/routes/account-activation.js | 6 +- app/styles/app/layouts/activation.scss | 73 +++++++++-- app/styles/app/layouts/billing.scss | 3 +- app/styles/app/layouts/profile.scss | 34 ++++- app/styles/app/modules/tooltips.scss | 16 --- app/styles/app/modules/travis-form.scss | 28 +++++ app/styles/app/modules/wizard.scss | 51 +++++++- app/styles/app/vars.scss | 1 + .../components/billing/first-plan.hbs | 17 ++- .../billing/payment-details-tab.hbs | 6 +- .../components/billing/summary-v2.hbs | 117 +++++++----------- app/templates/components/forms/form-field.hbs | 10 ++ app/templates/components/forms/form-radio.hbs | 19 +++ tests/acceptance/profile/billing-test.js | 5 - tests/acceptance/profile/plan-usage-test.js | 13 -- 18 files changed, 315 insertions(+), 139 deletions(-) create mode 100644 app/components/forms/form-radio.js create mode 100644 app/templates/components/forms/form-radio.hbs diff --git a/app/components/billing/first-plan.js b/app/components/billing/first-plan.js index 9bef7d8929..6b11c97ede 100644 --- a/app/components/billing/first-plan.js +++ b/app/components/billing/first-plan.js @@ -180,14 +180,13 @@ export default Component.extend({ this.flashes.success('Your account has been successfully activated'); } catch (error) { yield this.accounts.fetchV2Subscriptions.perform().then(() => { - if( this.accounts.user.subscription || this.accounts.user.v2subscription) { + if (this.accounts.user.subscription || this.accounts.user.v2subscription) { this.storage.clearBillingData(); this.storage.clearSelectedPlanId(); this.storage.wizardStep = 2; this.wizard.update.perform(2); this.router.transitionTo('account.repositories'); - } - else { + } else { this.handleError(); } }); diff --git a/app/components/forms/form-radio.js b/app/components/forms/form-radio.js new file mode 100644 index 0000000000..928cdf52e7 --- /dev/null +++ b/app/components/forms/form-radio.js @@ -0,0 +1,47 @@ +import Component from '@ember/component'; + +export default Component.extend({ + classNames: ['travis-form__field-radio'], + classNameBindings: [ + 'disabled:travis-form__field-radio--disabled', + 'checked:travis-form__field-radio--checked:travis-form__field-radio--unchecked' + ], + + disabled: false, + checked: false, + + onChange() {}, + onFocus() {}, + onBlur() {}, + onInit() {}, + + click() { + if (!this.disabled) + this.send('toggle'); + }, + + didInsertElement() { + this._super(...arguments); + this.onInit(this.elementId); + }, + + actions: { + + toggle() { + if (!this.disabled) + this.onChange(!this.checked); + }, + + focus() { + if (!this.disabled) + this.onFocus(); + }, + + blur() { + if (!this.disabled) + this.onBlur(); + } + + } + +}); diff --git a/app/components/org-item.js b/app/components/org-item.js index cc36ab4505..6156087f6f 100644 --- a/app/components/org-item.js +++ b/app/components/org-item.js @@ -22,6 +22,7 @@ export default Component.extend({ routeModel: computed('account.isOrganization', function () { let isOrganization = this.get('account.isOrganization'); - if (isOrganization) return this.account.login; + let login = this.account.login || this.account.name; + if (isOrganization) return login; }) }); diff --git a/app/routes/account-activation.js b/app/routes/account-activation.js index 42d902a00a..45205e3ca8 100644 --- a/app/routes/account-activation.js +++ b/app/routes/account-activation.js @@ -12,15 +12,15 @@ export default SimpleLayoutRoute.extend({ wizardStateService: service('wizard-state'), activate() { - if( this.storage.wizardStep < 1) { + if (this.storage.wizardStep < 1) { this.storage.wizardStep = 1; this.wizardStateService.update.perform(1); } }, deactivate() { - let step = this.storage.wizardStep; - // if (step == 2 || step == 3) this.transitionTo('/account/repositories'); + /* let step = this.storage.wizardStep; + if (step == 2 || step == 3) this.transitionTo('/account/repositories');*/ }, title: 'Travis CI - Select Plan', diff --git a/app/styles/app/layouts/activation.scss b/app/styles/app/layouts/activation.scss index 47d75bf568..e26fa10e00 100644 --- a/app/styles/app/layouts/activation.scss +++ b/app/styles/app/layouts/activation.scss @@ -12,7 +12,7 @@ } .layout-activation-section__inner { - width: 440px; + width: 473px; max-width: $max-width; margin: 0 auto; padding: $column-gutter/2; @@ -26,7 +26,7 @@ font-weight: 600; margin-bottom: 12px; margin-top: 10px; - color: $asphalt-grey; + color: black; } .plan-desc { @@ -45,12 +45,20 @@ color: $asphalt-grey; } + .billing-plans__box-v2--desc { + color: $cement-grey; + font-weight: 500; + font-size: 13px; + margin: 5px 0 12px 0; + white-space: nowrap; + } + .plan-change { font-size: 16px; margin:0px; padding: 0px; - top: 50%; + top: 50px; right: 0%; text-align: right; position: relative; @@ -58,8 +66,8 @@ } #first-plan-charge-info { - margin-top: 14px; - margin-bottom: 44px; + margin-top: 0px; + margin-bottom: 24px; font-size: 12px; } #first-plan-activate-button { @@ -68,25 +76,62 @@ .not-required-form-elem { width: 100%; - margin-bottom: 34px; + margin-bottom: 10px; } .form-elem { width: 100%; - margin-bottom: 14px; + margin-bottom: -8px; + } + + .form-elem-select { + margin-top: 5px; } .travis-form { .travis-form__field { + &.travis-form__field--error { + margin-bottom: 12px; + } + + .travis-form__error-message { + margin-top: 0px; + } + .travis-form__label { font-size: 14px; + + .travis-form__required-mark { + color: $brick-red; + margin-bottom: 14px; + } } + + .travis-form__field-checkbox { + } + + &--checked { + .travis-form__field-checkbox-wrapper { + .travis-form__field-checkbox-image--checked { + opacity: 1; + } + } + } + + &--unchecked { + .travis-form__field-checkbox-wrapper { + .travis-form__field-checkbox-image--unchecked { + opacity: 1; + } + } + } + } } .cc-form-elem { width: 100%; - margin-bottom: 52px; + margin-bottom: 42px; } .framed-form { @@ -98,7 +143,12 @@ font-size: 14px; font-family: $font-family-sans-serif; background: white; - margin-top: 30px; + margin-bottom: 12px; + } + + .framed-form-plan { + margin-top: 32px; + min-height: 80px; } .margin-left-s { @@ -163,4 +213,9 @@ .StripeElement--webkit-autofill { background-color: #FEFDE5 !important; } + + .icon-desc { + margin-bottom: 3px; + @include colorSVG($oxide-blue); + } } diff --git a/app/styles/app/layouts/billing.scss b/app/styles/app/layouts/billing.scss index f0a0b65056..401eaf40bd 100644 --- a/app/styles/app/layouts/billing.scss +++ b/app/styles/app/layouts/billing.scss @@ -500,7 +500,7 @@ } &__buttons { - margin-top: 16px; + margin-top: 0px; &--cancel { margin-left: 10px; @@ -689,6 +689,7 @@ .payment-details-header { color: $cement-grey; + margin-bottom: 30px; } .no-invoices { diff --git a/app/styles/app/layouts/profile.scss b/app/styles/app/layouts/profile.scss index cdc623232f..a611216ab3 100644 --- a/app/styles/app/layouts/profile.scss +++ b/app/styles/app/layouts/profile.scss @@ -464,20 +464,37 @@ section.billing { margin-top: -22px; } + .plan-price-container-manual-section { + margin-bottom: -38px; + display: flex; + flex-direction: column; + + } + + .plan-price-container-manual { + margin-top: auto; + } + + .plan-price-info { color: $oxide-blue; text-transform: none; - font-size: 3.5em; + font-size: 50px; font-weight: 300; margin-bottom: 2px; } .plan-price-label { + color: $dim-grey; align-self: flex-end; margin-bottom: 16px; margin-right: 5px; } + .plan-label-color { + color: $dim-grey; + } + .billing-section-link, .get-started-link { text-decoration: underline; @@ -490,8 +507,7 @@ section.billing { } .plan { - background-color: $pebble-grey; - margin: 18px; + margin: 0px; p { margin: 0.3em 0 0.3em 0; font-weight: 100; @@ -504,13 +520,23 @@ section.billing { } } + .plan-grey { + background-color: $pebble-grey; + padding: 18px; + padding-bottom: 0px; + } + .plan-grey-buttons { + padding-top: 0px; + margin-bottom: 0px; + } + .plan+h2 { margin-bottom: 0; } .plan-button-container { width: 100%; - height: 66px; + height: 52px; } .plan-buttons { diff --git a/app/styles/app/modules/tooltips.scss b/app/styles/app/modules/tooltips.scss index d9d5f041d2..c8b7c5dfb7 100644 --- a/app/styles/app/modules/tooltips.scss +++ b/app/styles/app/modules/tooltips.scss @@ -26,22 +26,6 @@ $tooltip-color: $asphalt-grey; } -.white-tooltip { - background-color: white; - color: #666666; - font-size: 18px; - font-weight: 400; - font-style: normal; - text-decoration: none; - border: 1px solid #666666; - padding-right: 25px; - padding-left: 25px; - margin-right:100px; - .ember-tooltip-arrow { - display: none; - } -} - body > .ember-tooltip { // Tweaks “View branch on GitHub” in components:my-build top: 6px !important; diff --git a/app/styles/app/modules/travis-form.scss b/app/styles/app/modules/travis-form.scss index c5d40d8e42..8214db6572 100644 --- a/app/styles/app/modules/travis-form.scss +++ b/app/styles/app/modules/travis-form.scss @@ -127,6 +127,34 @@ $select-multiple-spacing: 5px; } } + .travis-form__field-radio { + + display: flex; + justify-content: flex-start; + align-items: center; + cursor: pointer; + border: none; + + .travis-form__field-radio-wrapper { + display: inline-block; + border-radius: 50%; + padding: 2px; + line-height: 0; + background-color: transparent; + cursor: pointer; + margin-right: 0.5rem; + + } + + &--checked &__radio::before { + background-color: $oxide-blue; + } + + &--unchecked &__radio::before { + background-color: transparent; + } + } + .travis-form__field-checkbox { display: flex; justify-content: flex-start; diff --git a/app/styles/app/modules/wizard.scss b/app/styles/app/modules/wizard.scss index 2d02bdec17..e0dd2aefa8 100644 --- a/app/styles/app/modules/wizard.scss +++ b/app/styles/app/modules/wizard.scss @@ -49,6 +49,7 @@ .wizard-page-number { margin-left: 18px; + color: $asphalt-grey; } } @@ -58,6 +59,54 @@ .ember-popover { border: none; box-shadow: 0 0 10px 0px rgba(0, 0, 0, 0.15); - z-index: 50; + z-index: 51; + } + + .ember-popover[x-placement^="top"] .ember-popover-arrow { + border-top-color: white; + margin-left: -5px; + bottom: -39px; + z-index:51; + + &:after { + border: 15px solid transparent; + content: ""; + margin-left: -15px; + display: block; + position: absolute; + height: 15px; + width: 15px; + bottom: 5px; + z-index: -1; + background: transparent; + transform: rotate(45deg); + box-shadow: 2px 2px 3px rgba(0, 0, 0, 0.15); + } + } + + .ember-popover[x-placement^="bottom"] .ember-popover-arrow { + border-bottom-color: white; + margin-left: -5px; + top: -40px; + + &:before { + border: 10px solid transparent; + content: ""; + margin-left: -10px; + display: block; + position: absolute; + height: 12px; + top: 4px; + width: 12px; + bottom: 5px; + z-index: -1; + background: transparent; + transform: rotate(45deg); + box-shadow: -2px -2px 3px rgba(0, 0, 0, 0.15); + } + } + .ember-popover-arrow { + z-index: 11; + border: 20px solid transparent; } } diff --git a/app/styles/app/vars.scss b/app/styles/app/vars.scss index 09691a0f0b..05c2aa7170 100644 --- a/app/styles/app/vars.scss +++ b/app/styles/app/vars.scss @@ -25,6 +25,7 @@ $haze-yellow: #faf6db; $cement-grey: #9d9d9d; $dry-cement: lighten($cement-grey, 30); $asphalt-grey: #666666; +$dim-grey: #727272; $pebble-grey: #f1f1f1; $brick-orange: #ed7d5b; diff --git a/app/templates/components/billing/first-plan.hbs b/app/templates/components/billing/first-plan.hbs index 727916bbda..40ab2bfb5e 100644 --- a/app/templates/components/billing/first-plan.hbs +++ b/app/templates/components/billing/first-plan.hbs @@ -62,7 +62,7 @@
    -
    +
    @@ -88,13 +88,13 @@
    - Yes + Yes
    - No + No
    @@ -107,7 +107,7 @@
    {{/if}} -
    +

    {{this.selectedPlan.name}}

    {{#unless this.isTrial}} @@ -126,8 +126,8 @@

    Private & Open-Source repos

    -

    - Linux, Windows, macOS, FreeBSD +

    + Linux, Windows, macOS, FreeBSD

    {{#if this.selectedPlan.hasOSSCreditAddons}} @@ -175,7 +175,7 @@ We will charge you $1 and refund you in 7 days. This is needed to make sure your card is valid. By clicking on "Verify Your Account" you agree to Travis CI - Terms,Privacy + Terms and Privacy Policy. Your free Trial Plan ends on August 5,2022. If you cancel your free trial by that date you @@ -187,8 +187,7 @@ this.selectedPlan.isAnnual 'annualy' 'monthly'}} until you cancel the subscription. Previous charges won't be refunded when you cancel unless it's legally required. By clicking - on {{this.getActivateButtonText}} you agree o Travis CI Terms,Privacy + on {{this.getActivateButtonText}} you agree o Travis CI Terms and Privacy Policy. {{/if}}
    diff --git a/app/templates/components/billing/payment-details-tab.hbs b/app/templates/components/billing/payment-details-tab.hbs index 202db92914..9aafea816d 100644 --- a/app/templates/components/billing/payment-details-tab.hbs +++ b/app/templates/components/billing/payment-details-tab.hbs @@ -161,7 +161,7 @@ @onChange={{action (mut this.billingInfo.hasLocalRegistration) true}} as |Field| > - Yes + Yes
    @@ -172,7 +172,7 @@ @onChange={{action (mut this.billingInfo.hasLocalRegistration) false}} as |Field| > - No + No
    @@ -208,7 +208,6 @@
    {{#if this.invoices}} - {{#if this.hasV2Subscription}} {{#if this.v2subscription.isNotManual}} {{/if}} - {{else}}

    You do not have any invoices yet

    {{/if}} diff --git a/app/templates/components/billing/summary-v2.hbs b/app/templates/components/billing/summary-v2.hbs index 0e21ca6375..0ec39aaa04 100644 --- a/app/templates/components/billing/summary-v2.hbs +++ b/app/templates/components/billing/summary-v2.hbs @@ -24,56 +24,18 @@ {{/if}} {{/if}} -
    +
    - {{#if this.subscription.isManual}} -
    - You are on Manual Plan, you have - {{#if this.subscription.plan.isUnlimitedUsers}} - unlimited - {{else}} - {{this.subscription.addonUsage.user.totalCredits}} - {{/if}} - users, - {{#if this.subscription.plan.hasCreditAddons}} - {{this.subscription.addonUsage.private.totalCredits}} private credits - {{/if}} - {{#if (and this.subscription.plan.hasCreditAddons this.subscription.plan.hasOSSCreditAddons)}} - , - {{/if}} - {{#if this.subscription.plan.hasOSSCreditAddons}} - {{this.subscription.addonUsage.public.totalCredits}} public credits - {{/if}} - per month with access - to Linux, Windows, macOs and FreeBSD builds on the standard infrastructure size. -
    - {{/if}}
    - Current plan: -

    - {{#if this.subscription.isManual}} - Manual plan - {{#if this.subscription.isSubscribed}} - - active - - {{else if this.subscription.isExpired}} - - expired - - {{else}} - - {{this.subscription.status}} - - {{/if}} - {{else}} + Current plan: +

    {{#if this.subscription.plan}} {{this.subscription.plan.name}} {{else}} Unknown plan {{/if}} - {{#if (eq this.subscription.plan.planType 'hybrid')}} + {{#if this.subscription.isSubscribed}} active @@ -86,8 +48,21 @@ {{this.subscription.status}} - {{/if}} - {{else if this.subscription.recurringAddon}} + {{/if}} + + {{#if this.isSubscribed}} + Valid until {{moment-format this.subscription.validTo "MMMM D, YYYY"}} + {{else if this.isIncomplete}} + Incomplete + {{else if (and this.isComplete this.isCanceled)}} + Expires {{moment-from-now this.subscription.validTo}} on {{moment-format this.subscription.validTo "MMMM DD"}} + {{else if (and this.isComplete this.isExpired)}} + Expired {{moment-format this.subscription.validTo "MMMM D, YYYY"}} + {{/if}} + + {{#if this.subscription.recurringAddon}} {{#if this.isCanceled}} canceled @@ -104,18 +79,33 @@ {{this.subscription.recurringAddon.current_usage.status}} - {{/if}} + {{/if}} + + {{#if (and this.isComplete this.isCanceled)}} + Expires {{moment-from-now this.subscription.validTo}} on {{moment-format this.subscription.validTo "MMMM DD"}} + {{else if (eq this.subscription.recurringAddon.current_usage.status 'subscribed')}} + Valid until {{moment-format this.subscription.validToFromAddon "MMMM D, YYYY"}} + {{else if (eq this.subscription.recurringAddon.current_usage.status 'expired')}} + Expired {{moment-format this.subscription.validToFromAddon "MMMM D, YYYY"}} + {{/if}} + {{/if}} - {{/if}} +

    + {{#if this.subscription.isManual}} +
    +

    Manual plan

    +
    + {{/if}}
    - - {{#if this.subscription.plan.isTrial}} -
    -
    -
    - Total: -
    - {{#if this.subscription.plan }} - {{#if (or this.subscription.plan.isFree this.subscription.plan.isTrial)}} -

    - Free -

    - {{/if}} - {{/if}} -
    -
    - {{/if}}
    {{/if}} -
    + {{#if this.subscription.isNotManual }} +
    {{#if this.subscription.isGithub}}
    +
    + {{/if}} +
    - +
    {{#if (and this.subscription.isStripe this.showPlanInfo)}} {{#if this.subscription.hasUserLicenseAddons}} diff --git a/app/templates/components/forms/form-field.hbs b/app/templates/components/forms/form-field.hbs index c7f69a6567..e30ae1184b 100644 --- a/app/templates/components/forms/form-field.hbs +++ b/app/templates/components/forms/form-field.hbs @@ -67,6 +67,16 @@ onInit=(action "setFieldElementId") class="travis-form__field-component" ) + radio=(component "forms/form-radio" + form=this.form + checked=this.value + disabled=this.disabled + onFocus=(action "handleFocus") + onBlur=(action "handleBlur") + onChange=(action "handleChange") + onInit=(action "setFieldElementId") + class="travis-form__field-component" + ) switch=(component "forms/form-switch" form=this.form checked=this.value diff --git a/app/templates/components/forms/form-radio.hbs b/app/templates/components/forms/form-radio.hbs new file mode 100644 index 0000000000..0f7a1a6237 --- /dev/null +++ b/app/templates/components/forms/form-radio.hbs @@ -0,0 +1,19 @@ + + + + + +{{yield}} diff --git a/tests/acceptance/profile/billing-test.js b/tests/acceptance/profile/billing-test.js index 7d579b3fb2..b687bec79e 100644 --- a/tests/acceptance/profile/billing-test.js +++ b/tests/acceptance/profile/billing-test.js @@ -397,7 +397,6 @@ module('Acceptance | profile/billing', function (hooks) { await billingPaymentForm.completePayment.click(); assert.equal(profilePage.billing.plan.name, `${this.defaultV2Plan.name}`); - assert.dom(profilePage.billing.plan.description.scope).hasTextContaining(`${this.defaultV2Plan.privateCredits * (this.defaultV2Plan.isAnnual ? 12 : 1)} Credits`); }); test('view billing on an incomplete stripe plan', async function (assert) { @@ -965,7 +964,6 @@ module('Acceptance | profile/billing', function (hooks) { await billingPaymentForm.completePayment.click(); assert.equal(profilePage.billing.plan.name, `${this.defaultV2Plan.name}`); - assert.dom(profilePage.billing.plan.description.scope).hasTextContaining(`${this.defaultV2Plan.privateCredits * (this.defaultV2Plan.isAnnual ? 12 : 1)} Credits`); }); test('logs an exception when there is a subscription without a plan and handles unknowns', async function (assert) { @@ -1297,7 +1295,6 @@ module('Acceptance | profile/billing', function (hooks) { await billingPaymentForm.completePayment.click(); assert.equal(profilePage.billing.plan.name, `${this.defaultV2Plan.name}`); - assert.dom(profilePage.billing.plan.description.scope).hasTextContaining(`${this.defaultV2Plan.privateCredits * (this.defaultV2Plan.isAnnual ? 12 : 1)} Credits`); }); test('view billing tab when no organization subscription should fill form and transition to payment', async function (assert) { @@ -1364,7 +1361,6 @@ module('Acceptance | profile/billing', function (hooks) { await billingPaymentForm.completePayment.click(); assert.equal(profilePage.billing.plan.name, `${this.defaultV2Plan.name}`); - assert.dom(profilePage.billing.plan.description.scope).hasTextContaining(`${this.defaultV2Plan.privateCredits * (this.defaultV2Plan.isAnnual ? 12 : 1)} Credits`); }); test('create subscription with multiple emails', async function (assert) { @@ -1440,7 +1436,6 @@ module('Acceptance | profile/billing', function (hooks) { await billingPaymentForm.completePayment.click(); assert.equal(profilePage.billing.plan.name, `${this.defaultV2Plan.name}`); - assert.dom(profilePage.billing.plan.description.scope).hasTextContaining(`${this.defaultV2Plan.privateCredits * (this.defaultV2Plan.isAnnual ? 12 : 1)} Credits`); }); test('view plan with manual subscription', async function (assert) { diff --git a/tests/acceptance/profile/plan-usage-test.js b/tests/acceptance/profile/plan-usage-test.js index 6fd0eef9a2..ac5b630cba 100644 --- a/tests/acceptance/profile/plan-usage-test.js +++ b/tests/acceptance/profile/plan-usage-test.js @@ -66,7 +66,6 @@ module('Acceptance | profile/plan usage', function (hooks) { percySnapshot(assert); - assert.equal(profilePage.planUsage.page.uniquUsers.text, '1 Unique users who are running builds'); assert.equal(profilePage.planUsage.page.macMinutes.text, '3 min'); assert.equal(profilePage.planUsage.page.windowsMinutes.text, '2 min'); assert.equal(profilePage.planUsage.page.linuxMinutes.text, '1 min'); @@ -74,16 +73,4 @@ module('Acceptance | profile/plan usage', function (hooks) { assert.equal(profilePage.planUsage.page.minutesTotal.text, '6'); }); - test('click Check users activity', async function (assert) { - await profilePage.visit(); - await profilePage.planUsage.visit(); - await profilePage.planUsage.checkUserActivity.visit(); - - percySnapshot(assert); - - assert.equal(profilePage.planUsage.checkUserActivity.uniqueUsers.text, '1 active users'); - assert.equal(profilePage.planUsage.checkUserActivity.userName.text, 'user-login'); - assert.equal(profilePage.planUsage.checkUserActivity.minutesConsumed.text, '1'); - assert.equal(profilePage.planUsage.checkUserActivity.creditsConsumed.text, '0'); - }); }); From b10583879ac28b1c9f4f26dbcaea3164cc54ec8c Mon Sep 17 00:00:00 2001 From: gbarc80 Date: Thu, 23 Mar 2023 16:06:17 +0100 Subject: [PATCH 21/43] coupon --- app/components/billing/first-plan.js | 21 ++++++ app/styles/app/layouts/activation.scss | 71 +++++++++++++++++++ .../components/billing/first-plan.hbs | 38 +++++++++- 3 files changed, 129 insertions(+), 1 deletion(-) diff --git a/app/components/billing/first-plan.js b/app/components/billing/first-plan.js index 6b11c97ede..1a7f23b6d7 100644 --- a/app/components/billing/first-plan.js +++ b/app/components/billing/first-plan.js @@ -236,10 +236,31 @@ export default Component.extend({ this.flashes.error(message); }, + validateCoupon: task(function* () { + return yield this.store.findRecord('coupon', this.couponId, { + reload: true, + }); + }).drop(), + + coupon: reads('validateCoupon.last.value'), + couponError: reads('validateCoupon.last.error'), + isValidCoupon: reads('coupon.valid'), + couponHasError: computed('couponError', { + get() { + return !!this.couponError; + }, + set(key, value) { + return value; + } + }), + actions: { complete(stripeElement) { this.set('stripeElement', stripeElement); }, + handleCouponFocus() { + this.set('couponHasError', false); + }, clearCreditCardData() { this.subscription.set('creditCardInfo', null); diff --git a/app/styles/app/layouts/activation.scss b/app/styles/app/layouts/activation.scss index e26fa10e00..6be9e6df0e 100644 --- a/app/styles/app/layouts/activation.scss +++ b/app/styles/app/layouts/activation.scss @@ -29,6 +29,23 @@ color: black; } + .form-vatid { + margin-bottom: 8px; + } + + .form-label { + color: $cement-grey; + display: block; + text-transform: uppercase; + font-weight: 600; + font-size: 14px; + margin-bottom: 5px; + + .required { + color: $brick-red; + } + } + .plan-desc { font-size: 15px; margin-bottom: 12px; @@ -218,4 +235,58 @@ margin-bottom: 3px; @include colorSVG($oxide-blue); } + + .coupon-validation { + + height: 38px; + white-space: nowrap; + + &__input { + flex-basis: 85%; + } + + &__validate-button-wrapper { + flex-basis: 20%; + font-size: 14px; + margin-left: 10px; + align-items: center; + display: flex; + justify-content: flex-end; + height: 38px; + } + + .coupon-loading { + justify-content: center; + } + + &__validate-button { + padding: 11px; + } + + &__valid-coupon { + color: $ansi-green; + + .icon-passed { + @include colorSVG($ansi-green); + + color: inherit; + } + } + + &__invalid-coupon { + color: $ansi-red; + + .icon { + width: 20px; + height: 20px; + } + + .icon-warn { + @include colorSVG($ansi-red); + + color: inherit; + } + } + + } } diff --git a/app/templates/components/billing/first-plan.hbs b/app/templates/components/billing/first-plan.hbs index 40ab2bfb5e..ca0a334375 100644 --- a/app/templates/components/billing/first-plan.hbs +++ b/app/templates/components/billing/first-plan.hbs @@ -100,13 +100,49 @@ {{/if}} {{#if this.showVatField}} -
    +
    {{/if}} + coupon code +
    + +
    + + + +
    + {{#if this.validateCoupon.isRunning}} + + {{else if this.isValidCoupon}} +

    + + Coupon applied +

    + {{else if this.couponHasError}} +

    + + Coupon invalid +

    + {{else}} + + {{/if}} +
    +
    +
    +

    {{this.selectedPlan.name}}

    From c33ec59bb21a36b262ad78adcd6cff22243a48f4 Mon Sep 17 00:00:00 2001 From: gbarc80 Date: Fri, 24 Mar 2023 10:01:00 +0100 Subject: [PATCH 22/43] installation redirections, email banner update --- app/components/top-bar.js | 4 ++-- app/controllers/github-apps-installation.js | 15 ++++++++++++++- app/services/auth.js | 12 +++++++++++- app/services/storage/auth.js | 15 +++++++++++++++ 4 files changed, 42 insertions(+), 4 deletions(-) diff --git a/app/components/top-bar.js b/app/components/top-bar.js index 68664621ab..3b1bf4ac1f 100644 --- a/app/components/top-bar.js +++ b/app/components/top-bar.js @@ -31,8 +31,8 @@ export default Component.extend(InViewportMixin, { isUnconfirmed: computed('user.confirmedAt', function () { if (!this.user || (this.storage.wizardStep > 0 && this.storage.wizardStep <= 1) || - this.router.currentRoute == 'first_sync' || - this.router.currentRoute == 'github_apps_installation') + this.router.currentRouteName == 'first_sync' || + this.router.currentRouteName == 'github_apps_installation') return false; return !this.user.confirmedAt; }), diff --git a/app/controllers/github-apps-installation.js b/app/controllers/github-apps-installation.js index dd1d540e19..8dc2995e45 100644 --- a/app/controllers/github-apps-installation.js +++ b/app/controllers/github-apps-installation.js @@ -5,12 +5,15 @@ import { inject as service } from '@ember/service'; import config from 'travis/config/environment'; import { later } from '@ember/runloop'; import { Promise as EmberPromise } from 'rsvp'; +import { reads } from '@ember/object/computed'; const interval = config.intervals.githubAppsInstallationPolling; export default Controller.extend({ auth: service(), raven: service(), + localStorage: service('storage'), + storage: reads('localStorage.auth'), queryParams: ['installation_id'], @@ -18,8 +21,18 @@ export default Controller.extend({ maxRepetitions: 10, startPolling() { + let isSignup = false; + if (!this.installation_id) { + let data = this.storage.get('activeAccountInstallation'); + if (data) { + this.installation_id = data; + isSignup = true; + } + this.storage.set('activeAccountInstallation', null); + } this.initialDelayPromise().then(() => this.fetchPromise().then(() => { - this.transitionToRoute('first_sync'); + + this.transitionToRoute(isSignup ? 'first_sync' : 'account'); })); }, diff --git a/app/services/auth.js b/app/services/auth.js index 99396d94e0..9a3710effc 100644 --- a/app/services/auth.js +++ b/app/services/auth.js @@ -214,8 +214,12 @@ export default Service.extend({ if (!user || !token) throw new Error('No login data'); const userData = getProperties(user, USER_FIELDS); - this.validateUserData(userData, isBecome); + const installationData = getProperties(user, ['installation']); + if (installationData && installationData.installation) { + storage.set('activeAccountInstallation', installationData.installation); + } + this.validateUserData(userData, isBecome); const userRecord = pushUserToStore(this.store, userData); userRecord.set('authToken', token); @@ -314,6 +318,12 @@ export default Service.extend({ syncingDidChange: observer('isSyncing', 'currentUser', function () { const user = this.currentUser; if (user && user.get('isSyncing') && !user.get('syncedAt')) { + if (this.storage.get('activeAccountInstallation')) { + let installation = this.storage.get('activeAccountInstallation'); + if (installation) { + return this.router.transitionTo('github_apps_installation'); + } + } return this.router.transitionTo('first_sync'); } }), diff --git a/app/services/storage/auth.js b/app/services/storage/auth.js index 72cb6b3f47..6e1bc0aab4 100644 --- a/app/services/storage/auth.js +++ b/app/services/storage/auth.js @@ -80,6 +80,21 @@ export default Service.extend({ } }), + activeAccountInstallation: computed({ + get() { + return +storage.getItem('travis.auth.activeAccountInstallation'); + }, + set(key, id) { + if (id === null) { + storage.removeItem('travis.auth.activeAccountInstallation'); + return null; + } else { + storage.setItem('travis.auth.activeAccountInstallation', id); + return id; + } + } + }), + activeAccount: computed({ get() { const { accounts, activeAccountId } = this; From edf92a0575ac4c30e37619b7b0f726e9c5f6e8d4 Mon Sep 17 00:00:00 2001 From: gbarc80 Date: Fri, 24 Mar 2023 14:14:09 +0100 Subject: [PATCH 23/43] random ui updates --- app/styles/app/layouts/activation.scss | 11 ++++++++--- app/styles/app/layouts/profile.scss | 15 +++++++++------ app/templates/components/billing/first-plan.hbs | 8 ++++---- app/templates/components/billing/summary-v2.hbs | 10 +++++----- 4 files changed, 26 insertions(+), 18 deletions(-) diff --git a/app/styles/app/layouts/activation.scss b/app/styles/app/layouts/activation.scss index 6be9e6df0e..0b2df71c22 100644 --- a/app/styles/app/layouts/activation.scss +++ b/app/styles/app/layouts/activation.scss @@ -49,7 +49,7 @@ .plan-desc { font-size: 15px; margin-bottom: 12px; - margin-top: 10px; + margin-top: 4px; color: $asphalt-grey; } @@ -66,7 +66,7 @@ color: $cement-grey; font-weight: 500; font-size: 13px; - margin: 5px 0 12px 0; + margin: 5px 0 8px 0; white-space: nowrap; } @@ -75,12 +75,15 @@ font-size: 16px; margin:0px; padding: 0px; - top: 50px; + top: 40%; right: 0%; text-align: right; position: relative; color: $oxide-blue; } + .plan-change-expanded { + top: 52px; + } #first-plan-charge-info { margin-top: 0px; @@ -194,6 +197,8 @@ .text-subheader { font-size: 14px; color: #9D9D9D; + letter-spacing: 0px; + font-weight: 400; } .cc-form { diff --git a/app/styles/app/layouts/profile.scss b/app/styles/app/layouts/profile.scss index a611216ab3..047b87bdcf 100644 --- a/app/styles/app/layouts/profile.scss +++ b/app/styles/app/layouts/profile.scss @@ -465,10 +465,9 @@ section.billing { } .plan-price-container-manual-section { - margin-bottom: -38px; - display: flex; - flex-direction: column; - + margin-bottom: -18px; + margin-top: -64px; + display: relative; } .plan-price-container-manual { @@ -514,7 +513,7 @@ section.billing { } h3 { - font-size: $font-size-xl; + font-size: $font-size-ml; font-weight: $font-weight-light; margin-bottom: 0.2em; } @@ -539,8 +538,12 @@ section.billing { height: 52px; } + .plan-button-container-expanded { + margin-top: -20px; + } + .plan-buttons { - margin-left:18px; + margin-left:0px; // margin-top:-24px; // margin-bottom:14px; } diff --git a/app/templates/components/billing/first-plan.hbs b/app/templates/components/billing/first-plan.hbs index ca0a334375..b4150e1c60 100644 --- a/app/templates/components/billing/first-plan.hbs +++ b/app/templates/components/billing/first-plan.hbs @@ -198,11 +198,11 @@ {{/if}} {{/unless}} {{#if this.isTrial}} -

    Free plan valid one month

    +

    Free plan valid one month

    {{/if}}
    -
    @@ -220,10 +220,10 @@ {{else}} You'll be charged {{format-currency this.selectedPlan.startingPrice floor="true"}} {{ if - this.selectedPlan.isAnnual 'annualy' 'monthly'}} until you cancel the subscription. Previous + this.selectedPlan.isAnnual 'annualy' 'monthly'}} until you cancel your subscription. Previous charges won't be refunded when you cancel unless it's legally required. By clicking - on {{this.getActivateButtonText}} you agree o Travis CI Terms and Privacy + on "{{this.getActivateButtonText}}" you agree to Travis CI Terms and Privacy Policy. {{/if}}
    diff --git a/app/templates/components/billing/summary-v2.hbs b/app/templates/components/billing/summary-v2.hbs index 0ec39aaa04..4e97064bab 100644 --- a/app/templates/components/billing/summary-v2.hbs +++ b/app/templates/components/billing/summary-v2.hbs @@ -1,4 +1,4 @@ -
    +
    {{#if (not this.account.hasSubscriptionPermissions)}} @@ -103,8 +103,8 @@
    {{#if this.subscription.isManual }} -
    -
    +
    +
    Total:
    @@ -129,7 +129,7 @@
    {{/if}} {{#if this.subscription.isNotManual }} -
    +
    {{#if this.subscription.isGithub}} {{/if}} -
    +

    From 6340e638f8b2ec25ebec9c4263927d611efa5c04 Mon Sep 17 00:00:00 2001 From: gbarc80 Date: Fri, 24 Mar 2023 14:32:01 +0100 Subject: [PATCH 24/43] lint --- app/controllers/github-apps-installation.js | 1 - 1 file changed, 1 deletion(-) diff --git a/app/controllers/github-apps-installation.js b/app/controllers/github-apps-installation.js index 8dc2995e45..b14d4f213b 100644 --- a/app/controllers/github-apps-installation.js +++ b/app/controllers/github-apps-installation.js @@ -31,7 +31,6 @@ export default Controller.extend({ this.storage.set('activeAccountInstallation', null); } this.initialDelayPromise().then(() => this.fetchPromise().then(() => { - this.transitionToRoute(isSignup ? 'first_sync' : 'account'); })); }, From fee0e233748381f5e60853ab982ceb36bc2dc211 Mon Sep 17 00:00:00 2001 From: gbarc80 Date: Fri, 24 Mar 2023 14:48:31 +0100 Subject: [PATCH 25/43] total price hide --- app/styles/app/layouts/activation.scss | 1 + app/templates/components/billing/summary-v2.hbs | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/styles/app/layouts/activation.scss b/app/styles/app/layouts/activation.scss index 0b2df71c22..194dd5731d 100644 --- a/app/styles/app/layouts/activation.scss +++ b/app/styles/app/layouts/activation.scss @@ -92,6 +92,7 @@ } #first-plan-activate-button { margin-top: 44px; + margin-bottom: 44px; } .not-required-form-elem { diff --git a/app/templates/components/billing/summary-v2.hbs b/app/templates/components/billing/summary-v2.hbs index 4e97064bab..4a02e565b9 100644 --- a/app/templates/components/billing/summary-v2.hbs +++ b/app/templates/components/billing/summary-v2.hbs @@ -164,6 +164,7 @@ {{/if}} {{/if}}
    + {{#if this.showPlanInfo}}
    Total: @@ -174,7 +175,7 @@ {{/if}}
    - + {{/if}}
    {{/if}} From 09cf0e4f388921482065a57392674b46446b5d76 Mon Sep 17 00:00:00 2001 From: gbarc80 Date: Mon, 27 Mar 2023 13:48:20 +0200 Subject: [PATCH 26/43] empty invoices field, installation_id fix --- app/controllers/github-apps-installation.js | 6 +++++ app/styles/app/layouts/profile.scss | 24 +++++++++++++------ .../billing/payment-details-tab.hbs | 10 ++++++++ 3 files changed, 33 insertions(+), 7 deletions(-) diff --git a/app/controllers/github-apps-installation.js b/app/controllers/github-apps-installation.js index b14d4f213b..dbc46901b0 100644 --- a/app/controllers/github-apps-installation.js +++ b/app/controllers/github-apps-installation.js @@ -29,6 +29,12 @@ export default Controller.extend({ isSignup = true; } this.storage.set('activeAccountInstallation', null); + } else { + let data = this.storage.get('activeAccountInstallation'); + if (data) { + isSignup = true; + this.storage.set('activeAccountInstallation', null); + } } this.initialDelayPromise().then(() => this.fetchPromise().then(() => { this.transitionToRoute(isSignup ? 'first_sync' : 'account'); diff --git a/app/styles/app/layouts/profile.scss b/app/styles/app/layouts/profile.scss index 047b87bdcf..7a1e945798 100644 --- a/app/styles/app/layouts/profile.scss +++ b/app/styles/app/layouts/profile.scss @@ -645,6 +645,18 @@ section.billing { } } +.invoice-issue-banner { + padding: 10px; + box-shadow: 0 0 5px 1px rgba(0, 0, 0, 0.15); + border-radius: 5px; + width: 100%; +} + +.invoice-header { + color: $oxide-blue; + margin-bottom: 0; + } + section.invoices { font-size: $font-size-m; @@ -659,6 +671,11 @@ section.invoices { padding-bottom: 20px; } + .invoice-issue-banner { + margin-right: 46px; + width: 75%; + } + .invoice-select-year { margin: 0; } @@ -672,13 +689,6 @@ section.invoices { margin-right: 10px; } - .invoice-issue-banner { - padding: 10px; - box-shadow: 0 0 5px 1px rgba(0, 0, 0, 0.15); - margin-right: 46px; - border-radius: 5px; - width: 75%; - } table { width: 100%; diff --git a/app/templates/components/billing/payment-details-tab.hbs b/app/templates/components/billing/payment-details-tab.hbs index 9aafea816d..6e43bc15a0 100644 --- a/app/templates/components/billing/payment-details-tab.hbs +++ b/app/templates/components/billing/payment-details-tab.hbs @@ -224,6 +224,16 @@ /> {{/if}} {{else}} +

    Payment history

    +
    +

    + + Having trouble with your invoices? + + We’re happy to help + +

    +

    You do not have any invoices yet

    {{/if}} {{else}} From ee76cf65b76fca6aef6b9e51a8a734ae70b2534e Mon Sep 17 00:00:00 2001 From: gbarc80 Date: Tue, 28 Mar 2023 10:44:16 +0200 Subject: [PATCH 27/43] radio color,mail banner, double badge --- app/styles/app/modules/travis-form.scss | 27 ++++++++++++++++ .../components/billing/summary-v2.hbs | 32 +------------------ .../components/unconfirmed-user-banner.hbs | 4 +-- 3 files changed, 30 insertions(+), 33 deletions(-) diff --git a/app/styles/app/modules/travis-form.scss b/app/styles/app/modules/travis-form.scss index 8214db6572..3185a89780 100644 --- a/app/styles/app/modules/travis-form.scss +++ b/app/styles/app/modules/travis-form.scss @@ -146,6 +146,33 @@ $select-multiple-spacing: 5px; } + input[type="radio"]::before { + border-radius: 50%; + content: ""; + position: absolute; + width: 12px; + height: 12px; + background-color: white; + border: 2px solid #FFF; + appearance: none; + box-shadow: 0 0 0 1px $oxide-blue; + top: 6px; + left: 3px; + } + input[type="radio"]:checked::before { + border-radius: 50%; + content: ""; + position: absolute; + width: 12px; + height: 12px; + background-color: $oxide-blue; + border: 2px solid #FFF; + appearance: none; + box-shadow: 0 0 0 1px $oxide-blue; + top: 6px; + left: 3px; + } + &--checked &__radio::before { background-color: $oxide-blue; } diff --git a/app/templates/components/billing/summary-v2.hbs b/app/templates/components/billing/summary-v2.hbs index 4a02e565b9..b9e74ea7e1 100644 --- a/app/templates/components/billing/summary-v2.hbs +++ b/app/templates/components/billing/summary-v2.hbs @@ -62,37 +62,7 @@ Expired {{moment-format this.subscription.validTo "MMMM D, YYYY"}} {{/if}} - {{#if this.subscription.recurringAddon}} - {{#if this.isCanceled}} - - canceled - - {{else if (eq this.subscription.recurringAddon.current_usage.status 'subscribed')}} - - active - - {{else if (eq this.subscription.recurringAddon.current_usage.status 'expired')}} - - expired - - {{else}} - - {{this.subscription.recurringAddon.current_usage.status}} - - {{/if}} - - {{#if (and this.isComplete this.isCanceled)}} - Expires {{moment-from-now this.subscription.validTo}} on {{moment-format this.subscription.validTo "MMMM DD"}} - {{else if (eq this.subscription.recurringAddon.current_usage.status 'subscribed')}} - Valid until {{moment-format this.subscription.validToFromAddon "MMMM D, YYYY"}} - {{else if (eq this.subscription.recurringAddon.current_usage.status 'expired')}} - Expired {{moment-format this.subscription.validToFromAddon "MMMM D, YYYY"}} - {{/if}} - - {{/if}} - +

    {{#if this.subscription.isManual}}
    diff --git a/app/templates/components/unconfirmed-user-banner.hbs b/app/templates/components/unconfirmed-user-banner.hbs index 55f83fc34a..7589449625 100644 --- a/app/templates/components/unconfirmed-user-banner.hbs +++ b/app/templates/components/unconfirmed-user-banner.hbs @@ -1,3 +1,3 @@
    - Please check your email and confirm your account. If you need to generate a new confirmation email, please resend your confirmation email. -
    \ No newline at end of file + Please check your email and confirm your account, before that you will have limited build functions. If you need to generate a new confirmation email, please resend your confirmation email. +
    From 4b41c15325da4697809882f7e49a3465f4249367 Mon Sep 17 00:00:00 2001 From: gbarc80 Date: Thu, 30 Mar 2023 13:11:30 +0200 Subject: [PATCH 28/43] ui fixes - review 03.28 --- app/styles/app/layouts/billing.scss | 2 +- app/styles/app/layouts/profile.scss | 4 ++++ app/styles/app/pages/help.scss | 14 ++++++++++++++ .../components/billing/payment-details-tab.hbs | 2 +- app/templates/components/sync-button.hbs | 2 +- app/templates/help.hbs | 6 +++--- 6 files changed, 24 insertions(+), 6 deletions(-) diff --git a/app/styles/app/layouts/billing.scss b/app/styles/app/layouts/billing.scss index 401eaf40bd..79db9c8109 100644 --- a/app/styles/app/layouts/billing.scss +++ b/app/styles/app/layouts/billing.scss @@ -503,7 +503,7 @@ margin-top: 0px; &--cancel { - margin-left: 10px; + margin-left: -5px; margin-top: 5px; } diff --git a/app/styles/app/layouts/profile.scss b/app/styles/app/layouts/profile.scss index 7a1e945798..a97e4e4a62 100644 --- a/app/styles/app/layouts/profile.scss +++ b/app/styles/app/layouts/profile.scss @@ -652,6 +652,10 @@ section.billing { width: 100%; } +.invoice-issue-banner-no-invoices { + @include linkStyle; +} + .invoice-header { color: $oxide-blue; margin-bottom: 0; diff --git a/app/styles/app/pages/help.scss b/app/styles/app/pages/help.scss index 9b238c5782..c9b0a3d609 100644 --- a/app/styles/app/pages/help.scss +++ b/app/styles/app/pages/help.scss @@ -160,6 +160,15 @@ margin: 0; } + .header-billing { + font-size: 48px; + } + + .page-notice-billing { + font-size: 20px; + margin-bottom: 20px; + } + .help-duo-list { padding: 0; margin: 0 0 1.5rem 0; @@ -181,6 +190,11 @@ } } } + .help-duo-list-billing { + a { + font-size: 20px; + } + } .button { color: $oxide-blue; diff --git a/app/templates/components/billing/payment-details-tab.hbs b/app/templates/components/billing/payment-details-tab.hbs index 6e43bc15a0..129a7e88a1 100644 --- a/app/templates/components/billing/payment-details-tab.hbs +++ b/app/templates/components/billing/payment-details-tab.hbs @@ -226,7 +226,7 @@ {{else}}

    Payment history

    -

    +

    Having trouble with your invoices? diff --git a/app/templates/components/sync-button.hbs b/app/templates/components/sync-button.hbs index 5aa2cd07fa..f96c08072d 100644 --- a/app/templates/components/sync-button.hbs +++ b/app/templates/components/sync-button.hbs @@ -16,7 +16,7 @@ class="button--transparent" type="button" disabled={{if this.isSyncDisabled true false}} - onclick={{action "sync"}} + {{action "sync" bubbles=false}} > diff --git a/app/templates/help.hbs b/app/templates/help.hbs index 0e41368012..90445afa21 100644 --- a/app/templates/help.hbs +++ b/app/templates/help.hbs @@ -43,15 +43,15 @@ {{#if this.isBilling}}

    -

    +

    Hosted Billing

    -
    +
    If you have problems with billing, please check the Billing section in the documentation or fill out the form below
    -
      +
      • Billing Overview From 80313d718e4b5882436641a9952a1e68d4114754 Mon Sep 17 00:00:00 2001 From: gbarc80 Date: Thu, 30 Mar 2023 18:07:09 +0200 Subject: [PATCH 29/43] sync popup improvement --- app/styles/app/modules/sync-button.scss | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/styles/app/modules/sync-button.scss b/app/styles/app/modules/sync-button.scss index 66b9cdfe5b..69c3c2d778 100644 --- a/app/styles/app/modules/sync-button.scss +++ b/app/styles/app/modules/sync-button.scss @@ -62,11 +62,19 @@ border: none; padding: 0 0 0 0; } + .ember-popover[x-placement^="top"] .ember-popover-arrow { border-top-color: $asphalt-grey; margin-left: -5px; bottom: -13px; } + + .ember-popover[x-placement^="bottom"] .ember-popover-arrow { + border-bottom-color: $asphalt-grey; + margin-left: -2px; + top: -14px; + } + .ember-popover-arrow { border: 15px solid transparent; } From 167d2fe17c4ceb404f75c99469886be8ec476a1a Mon Sep 17 00:00:00 2001 From: GbArc Date: Wed, 24 May 2023 15:36:32 +0200 Subject: [PATCH 30/43] activation button fix --- app/components/billing/first-plan.js | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/app/components/billing/first-plan.js b/app/components/billing/first-plan.js index 1a7f23b6d7..8a439b4b55 100644 --- a/app/components/billing/first-plan.js +++ b/app/components/billing/first-plan.js @@ -125,7 +125,10 @@ export default Component.extend({ }), canActivate: computed('country', 'zipCode', 'address', 'creditCardOwner', 'city', 'stripeElement', 'billingEmail', function () { - return this.billingEmail && this.country && this.zipCode && this.address && this.creditCardOwner && this.stripeElement && this.city; + let valid = (val) => { + return !(val === null || val.trim() === ""); + } + return valid(this.billingEmail) && valid(this.country)&& valid(this.zipCode) && valid(this.address) && valid(this.creditCardOwner) && this.stripeElement && valid(this.city); }), createSubscription: task(function* () { @@ -206,6 +209,15 @@ export default Component.extend({ this.firstName = ''; this.lastName = ownerName; } + let empty = (val) => { + return val === null || val.trim() === ""; + } + if ( empty(this.lastName) || empty(this.address) || + empty(this.city) || empty(this.zipCode) || + empty(this.country) || empty(this.billingEmail) + ) { + throw new Error('Fill all required fields'); + } billingInfo.setProperties({ firstName: this.firstName, lastName: this.lastName, @@ -275,7 +287,9 @@ export default Component.extend({ }, subscribe() { - this.createSubscription.perform(); + if (this.canActivate) { + this.createSubscription.perform(); + } }, changeCountry(country) { this.set('country', country); From ef0700b9bb539e09db8e57d290a707716d0047b5 Mon Sep 17 00:00:00 2001 From: GbArc Date: Wed, 24 May 2023 19:43:37 +0200 Subject: [PATCH 31/43] lint --- app/components/billing/first-plan.js | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/app/components/billing/first-plan.js b/app/components/billing/first-plan.js index 8a439b4b55..4f027e7ba1 100644 --- a/app/components/billing/first-plan.js +++ b/app/components/billing/first-plan.js @@ -125,10 +125,11 @@ export default Component.extend({ }), canActivate: computed('country', 'zipCode', 'address', 'creditCardOwner', 'city', 'stripeElement', 'billingEmail', function () { - let valid = (val) => { - return !(val === null || val.trim() === ""); - } - return valid(this.billingEmail) && valid(this.country)&& valid(this.zipCode) && valid(this.address) && valid(this.creditCardOwner) && this.stripeElement && valid(this.city); + let valid = (val) => !(val === null || val.trim() === ''); + return valid(this.billingEmail) && valid(this.country) && + valid(this.zipCode) && valid(this.address) && + valid(this.creditCardOwner) && this.stripeElement && + valid(this.city); }), createSubscription: task(function* () { @@ -209,13 +210,11 @@ export default Component.extend({ this.firstName = ''; this.lastName = ownerName; } - let empty = (val) => { - return val === null || val.trim() === ""; - } - if ( empty(this.lastName) || empty(this.address) || - empty(this.city) || empty(this.zipCode) || - empty(this.country) || empty(this.billingEmail) - ) { + let empty = (val) => val === null || val.trim() === ''; + if (empty(this.lastName) || empty(this.address) || + empty(this.city) || empty(this.zipCode) || + empty(this.country) || empty(this.billingEmail) + ) { throw new Error('Fill all required fields'); } billingInfo.setProperties({ From 120be38d4bd82957af546f3072ba3b573cc7fd27 Mon Sep 17 00:00:00 2001 From: GbArc Date: Fri, 26 May 2023 09:56:35 +0200 Subject: [PATCH 32/43] user roles wip --- app/components/profile-nav.js | 26 ++++++++++++++++++------ app/components/repo-actions.js | 19 ++++++++++++++--- app/components/repo-show-tools.js | 14 +++++++++---- app/models/build.js | 10 ++++++++- app/routes/organization/billing.js | 2 +- app/routes/organization/plan_usage.js | 2 +- app/routes/organization/settings.js | 3 --- app/routes/settings.js | 2 +- app/templates/components/profile-nav.hbs | 4 ++-- 9 files changed, 60 insertions(+), 22 deletions(-) diff --git a/app/components/profile-nav.js b/app/components/profile-nav.js index 86821a46bb..19686ba171 100644 --- a/app/components/profile-nav.js +++ b/app/components/profile-nav.js @@ -65,27 +65,41 @@ export default Component.extend({ isOrganization: reads('model.isOrganization'), hasAdminPermissions: reads('model.permissions.admin'), isOrganizationAdmin: and('isOrganization', 'hasAdminPermissions'), - showOrganizationSettings: and('isOrganizationAdmin', 'isProVersion'), + showOrganizationSettings: computed('isOrganizationAdmin', 'isProVersion', function () { + const forOrganization = !this.isOrganization || this.model.permissions.settings_view; + return this.isOrganizationAdmin && this.isProVersion && forOrganization; + }), - showSubscriptionTab: computed('features.enterpriseVersion', 'model.isAssembla', 'model.isUser', function () { + showSubscriptionTab: computed('features.enterpriseVersion', 'model.isAssembla', 'model.isUser', 'isOrganization', function () { + const forOrganization = !this.isOrganization || + ((this.model.hasSubscription || this.model.hasV2Subscription) && this.model.permissions.plan_view) || + this.model.permissions.plan_create; const isAssemblaUser = this.model.isUser && this.model.isAssembla; const isEnterprise = this.features.get('enterpriseVersion'); - return !isEnterprise && !isAssemblaUser && !!billingEndpoint; + return !isEnterprise && !isAssemblaUser && !!billingEndpoint && !!forOrganization; }), showPaymentDetailsTab: computed('showSubscriptionTab', 'isOrganization', 'isOrganizationAdmin', 'model.isNotGithubOrManual', function () { + if (this.isOrganization) { - return this.showSubscriptionTab && this.isOrganizationAdmin && this.model.get('isNotGithubOrManual'); + const forOrganization = !this.isOrganization || this.model.permissions.billing_view; + + return this.showSubscriptionTab && this.isOrganizationAdmin && this.model.get('isNotGithubOrManual') && !!forOrganization; } else { return this.showSubscriptionTab && this.model.get('isNotGithubOrManual'); } }), - showPlanUsageTab: and('showSubscriptionTab', 'model.hasCredits'), + showPlanUsageTab: computed('showSubscriptionTab', 'model.hasCredits', function () { + const forOrganization = !this.isOrganization || this.model.permissions.plan_usage; + return this.showSubscriptionTab && this.model.hasCredits && forOrganization; + }), + usersUsage: computed('account.allowance.userUsage', 'addonUsage', function () { + const forOrganization = !this.isOrganization || this.model.permissions.plan_usage; const userUsage = this.model.allowance.get('userUsage'); if (userUsage === undefined) { return true; } - return userUsage; + return userUsage && forOrganization; }), wizardStep: reads('storage.wizardStep'), diff --git a/app/components/repo-actions.js b/app/components/repo-actions.js index 586e45c753..839187b17b 100644 --- a/app/components/repo-actions.js +++ b/app/components/repo-actions.js @@ -38,6 +38,7 @@ export default Component.extend({ userHasPermissionForRepo: computed('repo.id', 'user', 'user.permissions.[]', function () { let repo = this.repo; let user = this.user; + if (user && repo) { return user.hasAccessToRepo(repo); } @@ -68,9 +69,21 @@ export default Component.extend({ showPriority: true, showPrioritizeBuildModal: false, - canCancel: and('userHasPullPermissionForRepo', 'item.canCancel'), - canRestart: and('userHasPullPermissionForRepo', 'item.canRestart'), - canDebug: and('userHasPushPermissionForRepo', 'item.canDebug'), + canCancel: computed('userHasPullPermissionForRepo', 'item.canCancel', function () { + const forRepo = this.repo.permissions.build_cancel; + + return this.item.canCancel && forRepo; + }), + canRestart: computed('userHasPullPermissionForRepo', 'item.canRestart', function () { + const forRepo = this.repo.permissions.build_restart; + + return this.item.canRestart && forRepo; + }), + canDebug: computed('userHasPushPermissionForRepo', 'item.canDebug', function () { + const forRepo = this.repo.permissions.build_debug; + + return this.item.canDebug && forRepo; + }), isHighPriority: or('item.priority', 'item.build.priority'), isNotAlreadyHighPriority: not('isHighPriority'), hasPrioritizePermission: or('item.permissions.prioritize', 'item.build.permissions.prioritize'), diff --git a/app/components/repo-show-tools.js b/app/components/repo-show-tools.js index 9272a08d4c..02a3e5204d 100644 --- a/app/components/repo-show-tools.js +++ b/app/components/repo-show-tools.js @@ -27,12 +27,16 @@ export default Component.extend({ displaySettingsLink: computed('permissions.all', 'repo', function () { let repo = this.repo; - return this.permissions.hasPushPermission(repo); + const forRepo = repo.permissions.settings_read; + + return this.permissions.hasPushPermission(repo) && forRepo; }), displayCachesLink: computed('permissions.all', 'repo', function () { let repo = this.repo; - return this.permissions.hasPushPermission(repo) && config.endpoints.caches; + const forRepo = repo.permissions.cache_view; + + return this.permissions.hasPushPermission(repo) && config.endpoints.caches && forRepo; }), displayStatusImages: computed('permissions.all', 'repo', function () { @@ -49,11 +53,13 @@ export default Component.extend({ let canTriggerBuild = this.get('repo.permissions.create_request'); let enterprise = this.get('features.enterpriseVersion'); let pro = this.get('features.proVersion'); + const forRepo = this.repo.permissions.build_create; + if (enterprise || pro) { - return canTriggerBuild; + return canTriggerBuild && forRepo; } - return canTriggerBuild && migrationStatus !== 'migrated'; + return canTriggerBuild && migrationStatus !== 'migrated' && forRepo; } ), diff --git a/app/models/build.js b/app/models/build.js index b52e616ca3..4ad990173d 100644 --- a/app/models/build.js +++ b/app/models/build.js @@ -130,11 +130,17 @@ export default Model.extend(DurationCalculations, { }), canCancel: computed('jobs.@each.canCancel', function () { + if (this.repo.permissions && !this.repo.build_cancel) return false; + let jobs = this.jobs; return !isEmpty(jobs.filterBy('canCancel')); }), - canRestart: alias('isFinished'), + canRestart: computed('isFinished', function () { + if (this.repo.permissions && !this.repo.build_restart) return false; + + return this.isFinished; + }), cancel() { const url = `/build/${this.id}/cancel`; @@ -156,6 +162,8 @@ export default Model.extend(DurationCalculations, { }), canDebug: computed('jobs.[]', 'repo.private', function () { + if (this.repo.permissions && !this.repo.build_debug) return false; + let jobs = this.jobs; let repoPrivate = this.get('repo.private'); return jobs.get('length') === 1 && repoPrivate; diff --git a/app/routes/organization/billing.js b/app/routes/organization/billing.js index a28a8682bb..30a1ee308a 100644 --- a/app/routes/organization/billing.js +++ b/app/routes/organization/billing.js @@ -5,7 +5,7 @@ import AccountBillingMixin from 'travis/mixins/route/account/billing'; export default TravisRoute.extend(AccountBillingMixin, { model() { const organization = this.modelFor('organization'); - if (organization.permissions && organization.permissions.admin !== true) { + if (organization.permissions && organization.permissions.plan_view !== true) { this.transitionTo('organization.repositories', organization); } return hash({ diff --git a/app/routes/organization/plan_usage.js b/app/routes/organization/plan_usage.js index fcd1a4b612..9d99faadc2 100644 --- a/app/routes/organization/plan_usage.js +++ b/app/routes/organization/plan_usage.js @@ -5,7 +5,7 @@ import { hash } from 'rsvp'; export default TravisRoute.extend(AccountPlanUsageMixin, { model() { const organization = this.modelFor('organization'); - if (organization.permissions && organization.permissions.admin !== true) { + if (organization.permissions && organization.permissions.plan_usage !== true) { this.transitionTo('organization.repositories', organization); } return hash({ diff --git a/app/routes/organization/settings.js b/app/routes/organization/settings.js index 6083988bcd..c064bb0748 100644 --- a/app/routes/organization/settings.js +++ b/app/routes/organization/settings.js @@ -13,9 +13,6 @@ export default TravisRoute.extend({ model() { const organization = this.modelFor('organization'); - if (organization.permissions.admin !== true) { - this.transitionTo('organization.repositories', organization); - } const preferences = this.store.query('preference', { organization_id: organization.id }); return hash({ organization, preferences }); }, diff --git a/app/routes/settings.js b/app/routes/settings.js index 94e125871b..426ff8f8ad 100644 --- a/app/routes/settings.js +++ b/app/routes/settings.js @@ -65,7 +65,7 @@ export default TravisRoute.extend({ beforeModel() { const repo = this.modelFor('repo'); - const hasPushPermission = this.permissions.hasPushPermission(repo); + const hasPushPermission = this.permissions.settings_view; if (!hasPushPermission) { this.transitionTo('repo.index'); this.flashes.error('Your permissions are insufficient to access this repository\'s settings'); diff --git a/app/templates/components/profile-nav.hbs b/app/templates/components/profile-nav.hbs index 2e23e484db..e04e32fe6a 100644 --- a/app/templates/components/profile-nav.hbs +++ b/app/templates/components/profile-nav.hbs @@ -167,7 +167,7 @@
      • {{/if}} - {{#if (and this.showSubscriptionTab this.isOrganizationAdmin)}} + {{#if this.showSubscriptionTab}}
      • Plan @@ -181,7 +181,7 @@
      • {{/if}} - {{#if (and this.showPlanUsageTab this.isOrganizationAdmin)}} + {{#if this.showPlanUsageTab}}
      • Plan usage From 6d31388e79315a2197df35ad78b9d35623610f86 Mon Sep 17 00:00:00 2001 From: GbArc Date: Wed, 7 Jun 2023 08:17:50 +0200 Subject: [PATCH 33/43] logs, settings, caches --- app/components/log-content.js | 9 ++++++++- app/components/repo-show-tools.js | 1 - app/models/job.js | 26 +++++++++++++++++++++----- app/routes/caches.js | 9 +++++++++ app/routes/settings.js | 3 +-- 5 files changed, 39 insertions(+), 9 deletions(-) diff --git a/app/components/log-content.js b/app/components/log-content.js index c72db2c278..0816e54745 100644 --- a/app/components/log-content.js +++ b/app/components/log-content.js @@ -247,7 +247,14 @@ export default Component.extend({ }, toggleLog() { - this.toggleProperty('logIsVisible'); + let repo = this.get('job.repo'); + if (repo.permissions.log_view) { + this.toggleProperty('logIsVisible'); + } else { + if (this.logIsVisible) { + this.toggleProperty('logIsVisible'); + } + } }, toggleRemoveLogModal() { diff --git a/app/components/repo-show-tools.js b/app/components/repo-show-tools.js index 02a3e5204d..46dfe30660 100644 --- a/app/components/repo-show-tools.js +++ b/app/components/repo-show-tools.js @@ -55,7 +55,6 @@ export default Component.extend({ let pro = this.get('features.proVersion'); const forRepo = this.repo.permissions.build_create; - if (enterprise || pro) { return canTriggerBuild && forRepo; } diff --git a/app/models/job.js b/app/models/job.js index 4d07b6fe1e..5ef1ed722c 100644 --- a/app/models/job.js +++ b/app/models/job.js @@ -146,7 +146,9 @@ export default Model.extend(DurationCalculations, DurationAttributes, { }), clearLog() { - if (this.isLogAccessed) { + let access = this.repo.permissions.log_delete; + + if (this.isLogAccessed && access) { return this.log.clear(); } }, @@ -154,12 +156,22 @@ export default Model.extend(DurationCalculations, DurationAttributes, { canCancel: computed('isFinished', 'state', function () { let isFinished = this.isFinished; let state = this.state; + let access = this.repo.permissions.build_cancel; // not(isFinished) is insufficient since it will be true when state is undefined. - return !isFinished && !!state; + return !isFinished && !!state && access; }), - canRestart: alias('isFinished'), - canDebug: and('isFinished', 'repo.private'), + canRestart: computed('isFinished', function () { + let isFinished = this.isFinished; + let access = this.repo.permissions.build_restart; + return isFinished && access; + }), + canDebug: computed('isFinished', 'repo.private', function () { + let isFinished = this.isFinished; + let priv = this.repo.private; + let access = this.repo.permissions.build_debug; + return isFinished && priv && access; + }), cancel() { const url = `/job/${this.id}/cancel`; @@ -234,7 +246,11 @@ export default Model.extend(DurationCalculations, DurationAttributes, { } }), - canRemoveLog: not('log.removed'), + canRemoveLog: computed('log.removed', function () { + let removed = !!this.log.removed; + let access = this.repo.permissions.log_delete; + return !removed && access; + }), slug: computed('repo.slug', 'number', function () { let slug = this.get('repo.slug'); diff --git a/app/routes/caches.js b/app/routes/caches.js index 7e08519180..10d56aa330 100644 --- a/app/routes/caches.js +++ b/app/routes/caches.js @@ -11,6 +11,15 @@ export default TravisRoute.extend({ return this.controllerFor('repo').activate('caches'); }, + beforeModel() { + const repo = this.modelFor('repo'); + if (repo.permissions.cache_view) { + this.transitionTo('repo.index'); + this.flashes.error('Your permissions are insufficient to access this repository\'s cache'); + } + }, + + model() { const repo = this.modelFor('repo'); const url = `/repo/${repo.get('id')}/caches`; diff --git a/app/routes/settings.js b/app/routes/settings.js index 426ff8f8ad..0054111749 100644 --- a/app/routes/settings.js +++ b/app/routes/settings.js @@ -65,8 +65,7 @@ export default TravisRoute.extend({ beforeModel() { const repo = this.modelFor('repo'); - const hasPushPermission = this.permissions.settings_view; - if (!hasPushPermission) { + if (repo.permissions.settings_read) { this.transitionTo('repo.index'); this.flashes.error('Your permissions are insufficient to access this repository\'s settings'); } From a654110ad69f779194419a28a200e34861594982 Mon Sep 17 00:00:00 2001 From: GbArc Date: Wed, 7 Jun 2023 14:24:02 +0200 Subject: [PATCH 34/43] fixed the condition for settings --- app/routes/settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/routes/settings.js b/app/routes/settings.js index 0054111749..98d7d7ebee 100644 --- a/app/routes/settings.js +++ b/app/routes/settings.js @@ -65,7 +65,7 @@ export default TravisRoute.extend({ beforeModel() { const repo = this.modelFor('repo'); - if (repo.permissions.settings_read) { + if (!repo.permissions.settings_read) { this.transitionTo('repo.index'); this.flashes.error('Your permissions are insufficient to access this repository\'s settings'); } From 8ca05ca2516b04ee427e672ee08b22006fb86b23 Mon Sep 17 00:00:00 2001 From: GbArc Date: Fri, 9 Jun 2023 10:03:59 +0200 Subject: [PATCH 35/43] perm fixes/test fixes --- app/components/log-content.js | 9 +- app/components/profile-nav.js | 56 ++++---- app/components/repo-actions.js | 39 +++--- app/models/build.js | 6 - app/models/job.js | 23 +--- app/models/user.js | 7 + app/routes/caches.js | 2 +- mirage/config.js | 9 ++ mirage/factories/repository.js | 11 ++ .../acceptance/dashboard/repositories-test.js | 4 +- tests/acceptance/profile/billing-test.js | 126 +++++++++++++++++- tests/acceptance/repo/caches-test.js | 10 +- tests/acceptance/repo/settings-test.js | 6 +- tests/acceptance/repo/trigger-build-test.js | 5 +- .../components/repo-actions-test.js | 20 +-- tests/unit/serializers/repo-test.js | 3 + 16 files changed, 249 insertions(+), 87 deletions(-) diff --git a/app/components/log-content.js b/app/components/log-content.js index 0816e54745..cc103e3dbd 100644 --- a/app/components/log-content.js +++ b/app/components/log-content.js @@ -220,12 +220,13 @@ export default Component.extend({ return this.permissions.hasPermission(repo); }), - canRemoveLog: computed('job', 'job.canRemoveLog', 'hasPermission', function () { + canRemoveLog: computed('job', 'job.canRemoveLog', 'hasPermission', 'currentUser', function () { let job = this.job; let canRemoveLog = this.get('job.canRemoveLog'); let hasPermission = this.hasPermission; + let access = this.currentUser && this.currentUser.hasPermissionToRepo(this.get('job.repo'), 'log_delete'); if (job) { - return canRemoveLog && hasPermission; + return canRemoveLog && hasPermission && access; } }), @@ -247,8 +248,8 @@ export default Component.extend({ }, toggleLog() { - let repo = this.get('job.repo'); - if (repo.permissions.log_view) { + let access = this.currentUser && this.currentUser.hasPermissionToRepo(this.get('job.repo'), 'log_view'); + if (access) { this.toggleProperty('logIsVisible'); } else { if (this.logIsVisible) { diff --git a/app/components/profile-nav.js b/app/components/profile-nav.js index 19686ba171..3661363951 100644 --- a/app/components/profile-nav.js +++ b/app/components/profile-nav.js @@ -64,37 +64,45 @@ export default Component.extend({ isOrganization: reads('model.isOrganization'), hasAdminPermissions: reads('model.permissions.admin'), + hasPlanViewPermissions: reads('model.permissions.plan_view'), + hasPlanUsagePermissions: reads('model.permissions.plan_usage'), + hasPlanCreatePermissions: reads('model.permissions.plan_create'), + hasBillingViewPermissions: reads('model.permissions.billing_view'), + hasSettingsReadPermissions: reads('model.permissions.settings_read'), isOrganizationAdmin: and('isOrganization', 'hasAdminPermissions'), - showOrganizationSettings: computed('isOrganizationAdmin', 'isProVersion', function () { - const forOrganization = !this.isOrganization || this.model.permissions.settings_view; + showOrganizationSettings: computed('isOrganizationAdmin', 'isProVersion', 'hasSettingsReadPermissions', function () { + const forOrganization = !this.isOrganization || this.hasSettingsReadPermissions; return this.isOrganizationAdmin && this.isProVersion && forOrganization; }), - showSubscriptionTab: computed('features.enterpriseVersion', 'model.isAssembla', 'model.isUser', 'isOrganization', function () { - const forOrganization = !this.isOrganization || - ((this.model.hasSubscription || this.model.hasV2Subscription) && this.model.permissions.plan_view) || - this.model.permissions.plan_create; - const isAssemblaUser = this.model.isUser && this.model.isAssembla; - const isEnterprise = this.features.get('enterpriseVersion'); - return !isEnterprise && !isAssemblaUser && !!billingEndpoint && !!forOrganization; - }), - showPaymentDetailsTab: computed('showSubscriptionTab', 'isOrganization', 'isOrganizationAdmin', 'model.isNotGithubOrManual', function () { - - if (this.isOrganization) { - const forOrganization = !this.isOrganization || this.model.permissions.billing_view; - - return this.showSubscriptionTab && this.isOrganizationAdmin && this.model.get('isNotGithubOrManual') && !!forOrganization; - } else { - return this.showSubscriptionTab && this.model.get('isNotGithubOrManual'); - } - }), - showPlanUsageTab: computed('showSubscriptionTab', 'model.hasCredits', function () { - const forOrganization = !this.isOrganization || this.model.permissions.plan_usage; + showSubscriptionTab: computed('features.enterpriseVersion', 'hasPlanViewPermissions', + 'hasPlanCreatePermissions', 'model.isAssembla', 'model.isUser', + 'isOrganization', function () { + const forOrganization = !this.isOrganization || + ((this.model.hasSubscription || this.model.hasV2Subscription) && !!this.hasPlanViewPermissions) || + !!this.hasPlanCreatePermissions; + + const isAssemblaUser = this.model.isUser && this.model.isAssembla; + const isEnterprise = this.features.get('enterpriseVersion'); + return !isEnterprise && !isAssemblaUser && !!billingEndpoint && !!forOrganization; + }), + showPaymentDetailsTab: computed('showSubscriptionTab', 'isOrganization', 'isOrganizationAdmin', + 'hasBillingViewPermissions', 'model.isNotGithubOrManual', function () { + if (this.isOrganization) { + const forOrganization = !this.isOrganization || this.hasBillingViewPermissions; + + return this.showSubscriptionTab && this.isOrganizationAdmin && this.model.get('isNotGithubOrManual') && forOrganization; + } else { + return this.showSubscriptionTab && this.model.get('isNotGithubOrManual'); + } + }), + showPlanUsageTab: computed('showSubscriptionTab', 'model.hasCredits', 'hasPlanUsagePermissions', function () { + const forOrganization = !this.isOrganization || this.hasPlanUsagePermissions; return this.showSubscriptionTab && this.model.hasCredits && forOrganization; }), - usersUsage: computed('account.allowance.userUsage', 'addonUsage', function () { - const forOrganization = !this.isOrganization || this.model.permissions.plan_usage; + usersUsage: computed('account.allowance.userUsage', 'addonUsage', 'hasPlanUsagePermissions', function () { + const forOrganization = !this.isOrganization || this.hasPlanUsagePermissions; const userUsage = this.model.allowance.get('userUsage'); if (userUsage === undefined) { return true; diff --git a/app/components/repo-actions.js b/app/components/repo-actions.js index 839187b17b..2a8035111d 100644 --- a/app/components/repo-actions.js +++ b/app/components/repo-actions.js @@ -59,6 +59,27 @@ export default Component.extend({ return user.hasPushAccessToRepo(repo); } }), + userHasCancelPermissionForRepo: computed('repo.id', 'user', function () { + let repo = this.repo; + let user = this.user; + if (user && repo) { + return user.hasPermissionToRepo(repo, 'build_cancel'); + } + }), + userHasRestartPermissionForRepo: computed('repo.id', 'user', function () { + let repo = this.repo; + let user = this.user; + if (user && repo) { + return user.hasPermissionToRepo(repo, 'build_restart'); + } + }), + userHasDebugPermissionForRepo: computed('repo.id', 'user', function () { + let repo = this.repo; + let user = this.user; + if (user && repo) { + return user.hasPermissionToRepo(repo, 'build_debug'); + } + }), canOwnerBuild: reads('repo.canOwnerBuild'), ownerRoMode: reads('repo.owner.ro_mode'), @@ -69,21 +90,9 @@ export default Component.extend({ showPriority: true, showPrioritizeBuildModal: false, - canCancel: computed('userHasPullPermissionForRepo', 'item.canCancel', function () { - const forRepo = this.repo.permissions.build_cancel; - - return this.item.canCancel && forRepo; - }), - canRestart: computed('userHasPullPermissionForRepo', 'item.canRestart', function () { - const forRepo = this.repo.permissions.build_restart; - - return this.item.canRestart && forRepo; - }), - canDebug: computed('userHasPushPermissionForRepo', 'item.canDebug', function () { - const forRepo = this.repo.permissions.build_debug; - - return this.item.canDebug && forRepo; - }), + canCancel: and('userHasCancelPermissionForRepo', 'item.canCancel'), + canRestart: and('userHasRestartPermissionForRepo', 'item.canRestart'), + canDebug: and('userHasDebugPermissionForRepo', 'item.canDebug'), isHighPriority: or('item.priority', 'item.build.priority'), isNotAlreadyHighPriority: not('isHighPriority'), hasPrioritizePermission: or('item.permissions.prioritize', 'item.build.permissions.prioritize'), diff --git a/app/models/build.js b/app/models/build.js index 4ad990173d..56ab1bd34a 100644 --- a/app/models/build.js +++ b/app/models/build.js @@ -130,15 +130,11 @@ export default Model.extend(DurationCalculations, { }), canCancel: computed('jobs.@each.canCancel', function () { - if (this.repo.permissions && !this.repo.build_cancel) return false; - let jobs = this.jobs; return !isEmpty(jobs.filterBy('canCancel')); }), canRestart: computed('isFinished', function () { - if (this.repo.permissions && !this.repo.build_restart) return false; - return this.isFinished; }), @@ -162,8 +158,6 @@ export default Model.extend(DurationCalculations, { }), canDebug: computed('jobs.[]', 'repo.private', function () { - if (this.repo.permissions && !this.repo.build_debug) return false; - let jobs = this.jobs; let repoPrivate = this.get('repo.private'); return jobs.get('length') === 1 && repoPrivate; diff --git a/app/models/job.js b/app/models/job.js index 5ef1ed722c..9163139b90 100644 --- a/app/models/job.js +++ b/app/models/job.js @@ -2,7 +2,7 @@ import Model, { attr, belongsTo } from '@ember-data/model'; import { observer, computed } from '@ember/object'; -import { alias, and, equal, not, reads } from '@ember/object/computed'; +import { alias, and, equal, reads } from '@ember/object/computed'; import { inject as service } from '@ember/service'; import { isEqual } from '@ember/utils'; import { getOwner } from '@ember/application'; @@ -146,9 +146,7 @@ export default Model.extend(DurationCalculations, DurationAttributes, { }), clearLog() { - let access = this.repo.permissions.log_delete; - - if (this.isLogAccessed && access) { + if (this.isLogAccessed) { return this.log.clear(); } }, @@ -156,22 +154,14 @@ export default Model.extend(DurationCalculations, DurationAttributes, { canCancel: computed('isFinished', 'state', function () { let isFinished = this.isFinished; let state = this.state; - let access = this.repo.permissions.build_cancel; - // not(isFinished) is insufficient since it will be true when state is undefined. - return !isFinished && !!state && access; + return !isFinished && !!state; }), canRestart: computed('isFinished', function () { let isFinished = this.isFinished; - let access = this.repo.permissions.build_restart; - return isFinished && access; - }), - canDebug: computed('isFinished', 'repo.private', function () { - let isFinished = this.isFinished; - let priv = this.repo.private; - let access = this.repo.permissions.build_debug; - return isFinished && priv && access; + return isFinished; }), + canDebug: and('isFinished', 'repo.private'), cancel() { const url = `/job/${this.id}/cancel`; @@ -248,8 +238,7 @@ export default Model.extend(DurationCalculations, DurationAttributes, { canRemoveLog: computed('log.removed', function () { let removed = !!this.log.removed; - let access = this.repo.permissions.log_delete; - return !removed && access; + return !removed; }), slug: computed('repo.slug', 'number', function () { diff --git a/app/models/user.js b/app/models/user.js index 115e96b047..3a76cb65e6 100644 --- a/app/models/user.js +++ b/app/models/user.js @@ -73,6 +73,13 @@ export default Owner.extend({ } }, + hasPermissionToRepo(repo, permission) { + let permissions = repo.get ? repo.get('permissions') : null; + if (permissions) { + return permissions[permission] || false; + } + }, + sync(isOrganization) { this.set('isSyncing', true); this.set('applyFilterRepos', !isOrganization); diff --git a/app/routes/caches.js b/app/routes/caches.js index 10d56aa330..ca0a45d433 100644 --- a/app/routes/caches.js +++ b/app/routes/caches.js @@ -13,7 +13,7 @@ export default TravisRoute.extend({ beforeModel() { const repo = this.modelFor('repo'); - if (repo.permissions.cache_view) { + if (!repo.permissions.cache_view) { this.transitionTo('repo.index'); this.flashes.error('Your permissions are insufficient to access this repository\'s cache'); } diff --git a/mirage/config.js b/mirage/config.js index 777f0713e0..ce1f41aea1 100644 --- a/mirage/config.js +++ b/mirage/config.js @@ -191,6 +191,9 @@ export default function () { 'migrate': false, 'star': false, 'unstar': false, + 'build_cancel': true, + 'build_restart': true, + 'build_debug': true, 'create_cron': false, 'create_env_var': false, 'create_key_pair': false @@ -247,6 +250,9 @@ export default function () { 'migrate': false, 'star': false, 'unstar': false, + 'build_cancel': true, + 'build_restart': true, + 'build_debug': true, 'create_cron': false, 'create_env_var': false, 'create_key_pair': false @@ -303,6 +309,9 @@ export default function () { 'migrate': false, 'star': false, 'unstar': false, + 'build_cancel': true, + 'build_restart': true, + 'build_debug': true, 'create_cron': false, 'create_env_var': false, 'create_key_pair': false diff --git a/mirage/factories/repository.js b/mirage/factories/repository.js index c5ba686fb8..e4020be64f 100644 --- a/mirage/factories/repository.js +++ b/mirage/factories/repository.js @@ -20,6 +20,17 @@ export default Mirage.Factory.extend({ deactivate: false, star: false, unstar: false, + build_cancel: true, + build_restart: true, + build_debug: true, + log_view: true, + log_delete: true, + cache_view: true, + cache_delete: true, + settings_read: true, + settings_create: true, + settings_update: true, + settings_delete: true, create_request: false, create_cron: false, change_settings: false, diff --git a/tests/acceptance/dashboard/repositories-test.js b/tests/acceptance/dashboard/repositories-test.js index 13fcdda6d9..01a0c7a6b8 100644 --- a/tests/acceptance/dashboard/repositories-test.js +++ b/tests/acceptance/dashboard/repositories-test.js @@ -136,7 +136,9 @@ module('Acceptance | dashboard/repositories', function (hooks) { currentBuild: permissionBuild, defaultBranch: permissionBranch, permissions: { - create_request: true + create_request: true, + build_create: true, + build_restart: true } }); }); diff --git a/tests/acceptance/profile/billing-test.js b/tests/acceptance/profile/billing-test.js index b687bec79e..360ad16eaa 100644 --- a/tests/acceptance/profile/billing-test.js +++ b/tests/acceptance/profile/billing-test.js @@ -513,10 +513,21 @@ module('Acceptance | profile/billing', function (hooks) { created_at: new Date(2018, 7, 16), permissions: { read: true, - write: true + write: true, + plan_view: true, + billing_view: true, } }); + this.organization.permissions = { + plan_view: true, + plan_create: true, + billing_view: true, + billing_update: true, + plan_usage: true, + plan_invoices: true, + }; + this.subscription.owner = this.organization; this.subscription.source = 'github'; @@ -540,10 +551,21 @@ module('Acceptance | profile/billing', function (hooks) { created_at: new Date(2018, 7, 16), permissions: { read: true, - write: true + write: true, + plan_view: true, + billing_view: true, } }); + this.organization.permissions = { + plan_view: true, + plan_create: true, + billing_view: true, + billing_update: true, + plan_usage: true, + plan_invoices: true, + }; + this.subscription.owner = this.organization; this.subscription.source = 'github'; this.subscription.status = 'expired'; @@ -711,6 +733,12 @@ module('Acceptance | profile/billing', function (hooks) { test('switching to another account’s billing tab loads the subscription form properly', async function (assert) { this.organization.permissions = { createSubscription: true, + plan_view: true, + plan_create: true, + billing_view: true, + billing_update: true, + plan_usage: true, + plan_invoices: true, admin: true }; this.organization.save(); @@ -728,6 +756,12 @@ module('Acceptance | profile/billing', function (hooks) { test('view billing tab when trial has not started', async function (assert) { this.organization.permissions = { createSubscription: true, + plan_view: true, + plan_create: true, + billing_view: true, + billing_update: true, + plan_usage: true, + plan_invoices: true, admin: true }; this.organization.save(); @@ -744,6 +778,12 @@ module('Acceptance | profile/billing', function (hooks) { test('view billing tab with no create subscription permissions', async function (assert) { this.organization.permissions = { createSubscription: false, + plan_view: true, + plan_create: true, + billing_view: true, + billing_update: true, + plan_usage: true, + plan_invoices: true, admin: true }; this.organization.save(); @@ -759,6 +799,12 @@ module('Acceptance | profile/billing', function (hooks) { this.subscription = null; this.organization.permissions = { createSubscription: true, + plan_view: true, + plan_create: true, + billing_view: true, + billing_update: true, + plan_usage: true, + plan_invoices: true, admin: true }; this.organization.save(); @@ -791,6 +837,12 @@ module('Acceptance | profile/billing', function (hooks) { this.subscription = null; this.organization.permissions = { createSubscription: true, + plan_view: true, + plan_create: true, + billing_view: true, + billing_update: true, + plan_usage: true, + plan_invoices: true, admin: true }; this.organization.save(); @@ -824,6 +876,12 @@ module('Acceptance | profile/billing', function (hooks) { this.subscription = null; this.organization.permissions = { createSubscription: true, + plan_view: true, + plan_create: true, + billing_view: true, + billing_update: true, + plan_usage: true, + plan_invoices: true, admin: true }; this.organization.save(); @@ -859,6 +917,12 @@ module('Acceptance | profile/billing', function (hooks) { this.subscription = null; this.organization.permissions = { createSubscription: true, + plan_view: true, + plan_create: true, + billing_view: true, + billing_update: true, + plan_usage: true, + plan_invoices: true, admin: true }; this.organization.save(); @@ -889,7 +953,16 @@ module('Acceptance | profile/billing', function (hooks) { test('view billing tab on education account', async function (assert) { this.subscription = null; this.organization.attrs.education = true; - this.organization.permissions = { createSubscription: true, admin: true }; + this.organization.permissions = { + createSubscription: true, + plan_view: true, + plan_create: true, + billing_view: true, + billing_update: true, + plan_usage: true, + plan_invoices: true, + admin: true + }; this.organization.save(); await profilePage.visitOrganization({ name: 'org-login' }); @@ -914,7 +987,16 @@ module('Acceptance | profile/billing', function (hooks) { this.subscription = null; this.organization.attrs.education = true; - this.organization.permissions = { createSubscription: true, admin: true }; + this.organization.permissions = { + createSubscription: true, + plan_view: true, + plan_create: true, + billing_view: true, + billing_update: true, + plan_usage: true, + plan_invoices: true, + admin: true + }; this.organization.save(); await profilePage.visitOrganization({ name: 'org-login' }); @@ -1042,6 +1124,12 @@ module('Acceptance | profile/billing', function (hooks) { owner.inject('service:stripev3', 'config', 'config:stripe'); this.organization.permissions = { createSubscription: true, + plan_view: true, + plan_create: true, + billing_view: true, + billing_update: true, + plan_usage: true, + plan_invoices: true, admin: true }; this.organization.save(); @@ -1093,6 +1181,12 @@ module('Acceptance | profile/billing', function (hooks) { owner.inject('service:stripev3', 'config', 'config:stripe'); this.organization.permissions = { createSubscription: true, + plan_view: true, + plan_create: true, + billing_view: true, + billing_update: true, + plan_usage: true, + plan_invoices: true, admin: true }; this.organization.save(); @@ -1142,6 +1236,12 @@ module('Acceptance | profile/billing', function (hooks) { owner.inject('service:stripev3', 'config', 'config:stripe'); this.organization.permissions = { createSubscription: true, + plan_view: true, + plan_create: true, + billing_view: true, + billing_update: true, + plan_usage: true, + plan_invoices: true, admin: true }; this.organization.save(); @@ -1194,6 +1294,12 @@ module('Acceptance | profile/billing', function (hooks) { owner.inject('service:stripev3', 'config', 'config:stripe'); this.organization.permissions = { createSubscription: true, + plan_view: true, + plan_create: true, + billing_view: true, + billing_update: true, + plan_usage: true, + plan_invoices: true, admin: true }; this.organization.save(); @@ -1310,6 +1416,12 @@ module('Acceptance | profile/billing', function (hooks) { owner.inject('service:stripev3', 'config', 'config:stripe'); this.organization.permissions = { createSubscription: true, + plan_view: true, + plan_create: true, + billing_view: true, + billing_update: true, + plan_usage: true, + plan_invoices: true, admin: true }; this.organization.save(); @@ -1376,6 +1488,12 @@ module('Acceptance | profile/billing', function (hooks) { owner.inject('service:stripev3', 'config', 'config:stripe'); this.organization.permissions = { createSubscription: true, + plan_view: true, + plan_create: true, + billing_view: true, + billing_update: true, + plan_usage: true, + plan_invoices: true, admin: true }; this.organization.save(); diff --git a/tests/acceptance/repo/caches-test.js b/tests/acceptance/repo/caches-test.js index bbe54d221b..ed485e513a 100644 --- a/tests/acceptance/repo/caches-test.js +++ b/tests/acceptance/repo/caches-test.js @@ -41,9 +41,15 @@ module('Acceptance | repo caches', function (hooks) { size: 10061086 })); - this.repository = this.server.create('repository', { slug, caches }); - signInUser(currentUser); + + this.repository = this.server.create('repository', { + slug, + caches, + permissions: { cache_view: true, cache_delete: true }, + owner: {login: 'user-login', id: currentUser.id} + }); + }); test('view and delete caches', async function (assert) { diff --git a/tests/acceptance/repo/settings-test.js b/tests/acceptance/repo/settings-test.js index 12b3fd415c..5c0f0cea2a 100644 --- a/tests/acceptance/repo/settings-test.js +++ b/tests/acceptance/repo/settings-test.js @@ -33,7 +33,11 @@ module('Acceptance | repo settings', function (hooks) { slug: 'org-login/repository-name', private: true, permissions: { - admin: true + admin: true, + settings_read: true, + settings_create: true, + settings_delete: true, + settings_update: true }, owner: { login: 'org-login', id: 2 } }); diff --git a/tests/acceptance/repo/trigger-build-test.js b/tests/acceptance/repo/trigger-build-test.js index 3b25da6c66..45cc271cf9 100644 --- a/tests/acceptance/repo/trigger-build-test.js +++ b/tests/acceptance/repo/trigger-build-test.js @@ -34,7 +34,8 @@ module('Acceptance | repo/trigger build', function (hooks) { name: 'difference-engine', slug: 'adal/difference-engine', permissions: { - create_request: true + create_request: true, + build_create: true, }, owner: { login: 'adal', @@ -75,7 +76,7 @@ module('Acceptance | repo/trigger build', function (hooks) { }); test('trigger link is not visible to users without proper permissions', async function (assert) { - this.repo.update('permissions', { create_request: false }); + this.repo.update('permissions', { create_request: false, build_create: false }); await triggerBuildPage.visit({ owner: 'adal', repo: 'difference-engine' }); assert.ok(triggerBuildPage.popupTriggerLinkIsPresent, 'trigger build link is not rendered'); diff --git a/tests/integration/components/repo-actions-test.js b/tests/integration/components/repo-actions-test.js index 27faca0064..a323feedd3 100644 --- a/tests/integration/components/repo-actions-test.js +++ b/tests/integration/components/repo-actions-test.js @@ -21,10 +21,10 @@ module('Integration | Component | repo actions', function (hooks) { test('it shows cancel button only if job is cancelable', async function (assert) { this.set('job', EmberObject.create({ canCancel: true })); - await render(hbs`{{repo-actions userHasPullPermissionForRepo=false job=this.job}}`); + await render(hbs`{{repo-actions userHasCancelPermissionForRepo=false job=this.job}}`); assert.dom('button[aria-label="Cancel job"]').doesNotExist(); - await render(hbs`{{repo-actions userHasPullPermissionForRepo=true job=this.job}}`); + await render(hbs`{{repo-actions userHasCancelPermissionForRepo=true job=this.job}}`); assert.dom('button[aria-label="Cancel job"]').exists(); }); @@ -32,10 +32,10 @@ module('Integration | Component | repo actions', function (hooks) { test('it shows cancel button for build only if build is cancelable', async function (assert) { this.set('build', EmberObject.create({ canCancel: true })); - await render(hbs`{{repo-actions userHasPullPermissionForRepo=false build=this.build}}`); + await render(hbs`{{repo-actions userHasCancelPermissionForRepo=false build=this.build}}`); assert.dom('button[aria-label="Cancel build"]').doesNotExist(); - await render(hbs`{{repo-actions userHasPullPermissionForRepo=true build=this.build}}`); + await render(hbs`{{repo-actions userHasCancelPermissionForRepo=true build=this.build}}`); assert.dom('button[aria-label="Cancel build"]').exists(); }); @@ -43,36 +43,36 @@ module('Integration | Component | repo actions', function (hooks) { test('it shows restart button only if job is restartable', async function (assert) { this.set('job', EmberObject.create({ canRestart: true })); - await render(hbs`{{repo-actions userHasPullPermissionForRepo=false job=this.job}}`); + await render(hbs`{{repo-actions userHasRestartPermissionForRepo=false job=this.job}}`); assert.dom('button[aria-label="Restart job"]').doesNotExist(); - await render(hbs`{{repo-actions userHasPullPermissionForRepo=true canOwnerBuild=true job=this.job}}`); + await render(hbs`{{repo-actions userHasRestartPermissionForRepo=true canOwnerBuild=true job=this.job}}`); assert.dom('button[aria-label="Restart job"]').exists(); }); test('it does not show restart button if owner cannot build', async function (assert) { this.set('job', EmberObject.create({ canRestart: true })); - await render(hbs`{{repo-actions userHasPullPermissionForRepo=true canOwnerBuild=false job=this.job}}`); + await render(hbs`{{repo-actions userHasRestartPermissionForRepo=true canOwnerBuild=false job=this.job}}`); assert.dom('button[aria-label="Restart job"]').doesNotExist(); }); test('it shows prioritize button only if build is not prioritized, it is also not already started and if the org and user are having acces of it', async function (assert) { this.set('build', EmberObject.create({ canCancel: true })); - await render(hbs`{{repo-actions userHasPullPermissionForRepo=true userHasPushPermissionForRepo=true build=this.build canPrioritize=true}}`); + await render(hbs`{{repo-actions userHasCancelPermissionForRepo=true userHasPullPermissionForRepo=true userHasPushPermissionForRepo=true build=this.build canPrioritize=true}}`); assert.dom('.action-button-container').exists(); assert.dom('.action-button--prioritize').exists(); }); test('Prioritize button will be disabled in case user is not having push permission, ', async function (assert) { this.set('build', EmberObject.create({ canCancel: true })); - await render(hbs`{{repo-actions userHasPullPermissionForRepo=true userHasPushPermissionForRepo=false build=this.build canPrioritize=true}}`); + await render(hbs`{{repo-actions userHasCancelPermissionForRepo=true userHasPullPermissionForRepo=true userHasPushPermissionForRepo=false build=this.build canPrioritize=true}}`); assert.dom('.action-button--prioritize').isDisabled(); }); test('Prioritize button will not be disabled in case user is having push permission, ', async function (assert) { this.set('build', EmberObject.create({ canCancel: true })); - await render(hbs`{{repo-actions userHasPullPermissionForRepo=true userHasPushPermissionForRepo=true build=this.build canPrioritize=true}}`); + await render(hbs`{{repo-actions userHasCancelPermissionForRepo=true userHasPullPermissionForRepo=true userHasPushPermissionForRepo=true build=this.build canPrioritize=true}}`); assert.dom('.action-button--prioritize').isNotDisabled(); }); }); diff --git a/tests/unit/serializers/repo-test.js b/tests/unit/serializers/repo-test.js index 95e420f3b8..1af3731b99 100644 --- a/tests/unit/serializers/repo-test.js +++ b/tests/unit/serializers/repo-test.js @@ -44,6 +44,9 @@ module('Unit | Serializer | repo', function (hooks) { 'deactivate': true, 'star': true, 'unstar': true, + 'build_cancel': true, + 'build_restart': true, + 'build_debug': true, 'create_cron': true, 'create_env_var': true, 'create_key_pair': true, From 5b530c34922546a28807eeeae40e459cd88eef89 Mon Sep 17 00:00:00 2001 From: GbArc Date: Fri, 23 Jun 2023 11:57:21 +0200 Subject: [PATCH 36/43] usage banner fix, settings from profile access check --- app/components/github-apps-repository.js | 3 ++- app/components/profile-nav.js | 5 ++--- app/templates/components/repo-actions.hbs | 4 ---- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/app/components/github-apps-repository.js b/app/components/github-apps-repository.js index 5025b17d36..7cb594f5d1 100644 --- a/app/components/github-apps-repository.js +++ b/app/components/github-apps-repository.js @@ -28,7 +28,8 @@ export default Component.extend({ hasSettingsPermission: computed('permissions.all', 'repository', function () { let repo = this.repository; - return this.permissions.hasPushPermission(repo); + let forRepo = (repo.owner.id == this.user.id && repo.ownerType == 'user') || ((repo.shared || repo.ownerType != 'user') && repo.permissions.settings_read); + return this.permissions.hasPushPermission(repo) && forRepo; }), toggleRepositoryTask: task(function* () { diff --git a/app/components/profile-nav.js b/app/components/profile-nav.js index 3661363951..648e9dd613 100644 --- a/app/components/profile-nav.js +++ b/app/components/profile-nav.js @@ -101,13 +101,12 @@ export default Component.extend({ return this.showSubscriptionTab && this.model.hasCredits && forOrganization; }), - usersUsage: computed('account.allowance.userUsage', 'addonUsage', 'hasPlanUsagePermissions', function () { - const forOrganization = !this.isOrganization || this.hasPlanUsagePermissions; + usersUsage: computed('account.allowance.userUsage', 'addonUsage', function () { const userUsage = this.model.allowance.get('userUsage'); if (userUsage === undefined) { return true; } - return userUsage && forOrganization; + return userUsage; }), wizardStep: reads('storage.wizardStep'), diff --git a/app/templates/components/repo-actions.hbs b/app/templates/components/repo-actions.hbs index dfb1d6ea2e..3da5b803e6 100644 --- a/app/templates/components/repo-actions.hbs +++ b/app/templates/components/repo-actions.hbs @@ -124,10 +124,6 @@
    {{/if}} {{/if}} -{{else}} - {{#if this.userHasPullPermissionForRepo}} - - {{/if}} {{/if}} Date: Fri, 23 Jun 2023 13:25:36 +0200 Subject: [PATCH 37/43] tests --- app/components/github-apps-repository.js | 3 ++- tests/acceptance/profile/basic-layout-test.js | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/components/github-apps-repository.js b/app/components/github-apps-repository.js index 7cb594f5d1..a151a768d4 100644 --- a/app/components/github-apps-repository.js +++ b/app/components/github-apps-repository.js @@ -28,7 +28,8 @@ export default Component.extend({ hasSettingsPermission: computed('permissions.all', 'repository', function () { let repo = this.repository; - let forRepo = (repo.owner.id == this.user.id && repo.ownerType == 'user') || ((repo.shared || repo.ownerType != 'user') && repo.permissions.settings_read); + let forRepo = (repo.owner.id == this.user.id && repo.ownerType == 'user') || + ((repo.shared || repo.ownerType != 'user') && repo.permissions.settings_read); return this.permissions.hasPushPermission(repo) && forRepo; }), diff --git a/tests/acceptance/profile/basic-layout-test.js b/tests/acceptance/profile/basic-layout-test.js index dd5ffdd399..a5dcedcdf1 100644 --- a/tests/acceptance/profile/basic-layout-test.js +++ b/tests/acceptance/profile/basic-layout-test.js @@ -141,7 +141,8 @@ module('Acceptance | profile/basic layout', function (hooks) { managed_by_installation: true, private: false, permissions: { - admin: true + admin: true, + settings_read: true }, }); @@ -155,7 +156,8 @@ module('Acceptance | profile/basic layout', function (hooks) { managed_by_installation: true, private: true, permissions: { - admin: false + admin: false, + settings_read: true } }); From 9b2e68377076dc30571832cdf39f2260fb458222 Mon Sep 17 00:00:00 2001 From: GbArc Date: Fri, 14 Jul 2023 14:29:41 +0200 Subject: [PATCH 38/43] selected plan up --- app/templates/components/billing/selected-plan.hbs | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/templates/components/billing/selected-plan.hbs b/app/templates/components/billing/selected-plan.hbs index 32f99b73e3..627825d3bc 100644 --- a/app/templates/components/billing/selected-plan.hbs +++ b/app/templates/components/billing/selected-plan.hbs @@ -30,11 +30,9 @@ {{pluralize this.selectedPlan.concurrencyLimit "concurrent job"}}

    {{/if}} - {{#if (this.hasPlanChangePermission) }} - {{/if}}
    From c5fdd380f256133c05ab53e9ca9e44854b78cd21 Mon Sep 17 00:00:00 2001 From: GbArc Date: Fri, 14 Jul 2023 14:59:23 +0200 Subject: [PATCH 39/43] lint --- app/components/billing/authorization.js | 3 +-- app/components/billing/payment-details-tab.js | 4 ++-- app/components/profile-nav.js | 4 ++-- app/models/repo.js | 2 +- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/app/components/billing/authorization.js b/app/components/billing/authorization.js index 1927d8ebb2..0baa0c7b66 100644 --- a/app/components/billing/authorization.js +++ b/app/components/billing/authorization.js @@ -23,7 +23,6 @@ export default Component.extend({ requiresSource: equal('subscription.paymentIntent.status', 'requires_source'), lastPaymentIntentError: reads('subscription.paymentIntent.last_payment_error'), retryAuthorizationClientSecret: reads('subscription.paymentIntent.client_secret'), - hasSubscriptionPermissions: reads('account.hasSubscriptionPermissions'), notChargeInvoiceSubscription: not('subscription.chargeUnpaidInvoices.lastSuccessful.value'), freeV2Plan: equal('subscription.plan.startingPrice', 0), isSubscribed: reads('subscription.isSubscribed'), @@ -33,7 +32,7 @@ export default Component.extend({ return this.isSubscribed && this.hasSubscriptionPermissions && !this.freeV2Plan && !this.isTrial; }), - hasSubscriptionPermissions: computed('account.hasSubscriptionPermissions', 'account.permissions', function() { + hasSubscriptionPermissions: computed('account.hasSubscriptionPermissions', 'account.permissions', function () { return this.account.hasSubscriptionPermissions && (!this.account.isOrganization || this.account.permissions.plan_create); }), cancelSubscriptionLoading: reads('subscription.cancelSubscription.isRunning'), diff --git a/app/components/billing/payment-details-tab.js b/app/components/billing/payment-details-tab.js index 21aa7bfaf1..0b17246a26 100644 --- a/app/components/billing/payment-details-tab.js +++ b/app/components/billing/payment-details-tab.js @@ -39,10 +39,10 @@ export default Component.extend({ isV2SubscriptionEmpty: empty('v2subscription'), isSubscriptionEmpty: empty('v1subscription'), isSubscriptionsEmpty: and('isSubscriptionEmpty', 'isV2SubscriptionEmpty'), - canViewBilling: computed('model', function() { + canViewBilling: computed('model', function () { return !this.account.isOrganization || this.account.permissions.billing_view; }), - canEditBilling: computed('model', function() { + canEditBilling: computed('model', function () { return !this.account.isOrganization || this.account.permissions.billing_update; }), hasV2Subscription: not('isV2SubscriptionEmpty'), diff --git a/app/components/profile-nav.js b/app/components/profile-nav.js index 1dc84a893b..18541df971 100644 --- a/app/components/profile-nav.js +++ b/app/components/profile-nav.js @@ -92,7 +92,7 @@ export default Component.extend({ if (this.isOrganization) { const forOrganization = !this.isOrganization || this.hasBillingViewPermissions || this.hasInvoicesViewPermissions; - return this.showSubscriptionTab && this.model.get('isNotGithubOrManual') && (this.isOrganizationAdmin || forOrganization ); + return this.showSubscriptionTab && this.model.get('isNotGithubOrManual') && (this.isOrganizationAdmin || forOrganization); } else { return this.showSubscriptionTab && this.model.get('isNotGithubOrManual'); } @@ -103,7 +103,7 @@ export default Component.extend({ }), usersUsage: computed('account.allowance.userUsage', 'addonUsage', 'hasPlanUsagePermissions', function () { - const forOrganization = !this.isOrganization || this.hasPlanUsagePermissions; + //const forOrganization = !this.isOrganization || this.hasPlanUsagePermissions; const userUsage = this.model.allowance.get('userUsage'); if (userUsage === undefined) { return true; diff --git a/app/models/repo.js b/app/models/repo.js index 7e7170fbf0..c0a6cebd43 100644 --- a/app/models/repo.js +++ b/app/models/repo.js @@ -212,7 +212,7 @@ const Repo = VcsEntity.extend({ if (!this.auth.signedIn) return {}; const hasPermissions = this.permissions.settings_read; - if (hasPermissions === false) return {} + if (hasPermissions === false) return {}; try { const response = yield this.api.get(`/repo/${this.id}/settings`); return this._convertV3SettingsToV2(response.settings); From 8237b79e9cf6c8b38b94ed098ccfebc1b289e58c Mon Sep 17 00:00:00 2001 From: GbArc Date: Fri, 14 Jul 2023 15:13:53 +0200 Subject: [PATCH 40/43] lint --- app/components/profile-nav.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/components/profile-nav.js b/app/components/profile-nav.js index 18541df971..3dcd807d58 100644 --- a/app/components/profile-nav.js +++ b/app/components/profile-nav.js @@ -103,7 +103,7 @@ export default Component.extend({ }), usersUsage: computed('account.allowance.userUsage', 'addonUsage', 'hasPlanUsagePermissions', function () { - //const forOrganization = !this.isOrganization || this.hasPlanUsagePermissions; + // const forOrganization = !this.isOrganization || this.hasPlanUsagePermissions; const userUsage = this.model.allowance.get('userUsage'); if (userUsage === undefined) { return true; From 5e1c0041e270c8fc652e19462e0a0278e26e13d7 Mon Sep 17 00:00:00 2001 From: GbArc Date: Fri, 14 Jul 2023 18:37:06 +0200 Subject: [PATCH 41/43] activate update --- app/components/github-apps-repository.js | 7 +++++++ app/templates/components/github-apps-repository.hbs | 6 ++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/app/components/github-apps-repository.js b/app/components/github-apps-repository.js index a151a768d4..0b880dc8b4 100644 --- a/app/components/github-apps-repository.js +++ b/app/components/github-apps-repository.js @@ -26,6 +26,13 @@ export default Component.extend({ return this.user && vcsLinks.accessSettingsUrl(this.user.vcsType, { owner: this.user.login }); }), + hasActivatePermission: computed('permissions.all', 'repository', function () { + let repo = this.repository; + let forRepo = (repo.owner.id == this.user.id && repo.ownerType == 'user') || + ((repo.shared || repo.ownerType != 'user') && repo.permissions.activate); + return forRepo; + }), + hasSettingsPermission: computed('permissions.all', 'repository', function () { let repo = this.repository; let forRepo = (repo.owner.id == this.user.id && repo.ownerType == 'user') || diff --git a/app/templates/components/github-apps-repository.hbs b/app/templates/components/github-apps-repository.hbs index d8b2c8d310..cd9feed0d5 100644 --- a/app/templates/components/github-apps-repository.hbs +++ b/app/templates/components/github-apps-repository.hbs @@ -13,13 +13,15 @@ {{/if}} -{{#if this.hasSettingsPermission}} +{{#if this.hasActivatePermission}} {{#if this.isNotMatchGithub}} {{/if}} +{{/if}} +{{#if this.hasSettingsPermission}}
    -{{/if}} \ No newline at end of file +{{/if}} From a61c1602105ff5b6df7bd6060e06b919dd9e6554 Mon Sep 17 00:00:00 2001 From: Murtaza Khan Date: Sat, 28 Oct 2023 15:02:00 +0500 Subject: [PATCH 42/43] added missing check --- app/templates/components/billing/payment-details-tab.hbs | 1 + 1 file changed, 1 insertion(+) diff --git a/app/templates/components/billing/payment-details-tab.hbs b/app/templates/components/billing/payment-details-tab.hbs index 3dcfd06922..f62b1392fa 100644 --- a/app/templates/components/billing/payment-details-tab.hbs +++ b/app/templates/components/billing/payment-details-tab.hbs @@ -5,6 +5,7 @@ by placing test fee - $1, which will be returned to you within a week.

    + {{#if this.canViewBilling }}
    From 3817483c4352ca5feb727bb35b70961ac2f008b5 Mon Sep 17 00:00:00 2001 From: GbArc Date: Thu, 14 Dec 2023 12:56:23 +0100 Subject: [PATCH 43/43] fixed admin settings permissions --- app/components/profile-nav.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/components/profile-nav.js b/app/components/profile-nav.js index 3dcd807d58..8ab652f27c 100644 --- a/app/components/profile-nav.js +++ b/app/components/profile-nav.js @@ -70,10 +70,11 @@ export default Component.extend({ hasBillingViewPermissions: reads('model.permissions.billing_view'), hasInvoicesViewPermissions: reads('model.permissions.plan_invoices'), hasSettingsReadPermissions: reads('model.permissions.settings_read'), + hasSettingsCreatePermissions: reads('model.permissions.settings_create'), isOrganizationAdmin: and('isOrganization', 'hasAdminPermissions'), - showOrganizationSettings: computed('isOrganizationAdmin', 'isProVersion', 'hasSettingsReadPermissions', function () { - const forOrganization = !this.isOrganization || this.hasSettingsReadPermissions; - return this.isOrganizationAdmin && this.isProVersion && forOrganization; + showOrganizationSettings: computed('isOrganizationAdmin', 'isProVersion', 'hasSettingsCreatePermissions', function () { + const forOrganization = !this.isOrganization || this.hasSettingsCreatePermissions; + return this.isProVersion && forOrganization; }), showSubscriptionTab: computed('features.enterpriseVersion', 'hasPlanViewPermissions',