diff --git a/app/assets/javascripts/usage_quotas/index.js b/app/assets/javascripts/usage_quotas/index.js index 414fbc717704dc96d5d02816047ce9323b0da656..67cf14b992d8ea4b8540442602c237c9ea804e6a 100644 --- a/app/assets/javascripts/usage_quotas/index.js +++ b/app/assets/javascripts/usage_quotas/index.js @@ -28,7 +28,7 @@ export default () => { apolloProvider, provide: { namespaceName, - tabs: usageQuotasTabsMetadata, + tabs: usageQuotasTabsMetadata.filter(Boolean), ...usageQuotasViewProvideData, }, render(createElement) { diff --git a/ee/app/assets/javascripts/usage_quotas/constants.js b/ee/app/assets/javascripts/usage_quotas/constants.js index 6c28045bb7e80535d09178fd19df78ae27291f5d..783461e1f57c07a6225a34d92ee23dfcc2625636 100644 --- a/ee/app/assets/javascripts/usage_quotas/constants.js +++ b/ee/app/assets/javascripts/usage_quotas/constants.js @@ -4,3 +4,4 @@ export const USAGE_BY_MONTH_HEADER = s__('UsageQuota|Usage by month'); export const USAGE_BY_PROJECT_HEADER = s__('UsageQuota|Usage by project'); export const PIPELINES_TAB_METADATA_EL_SELECTOR = '#js-pipeline-usage-app'; +export const SEATS_TAB_METADATA_EL_SELECTOR = '#js-seat-usage-app'; diff --git a/ee/app/assets/javascripts/usage_quotas/group_view_metadata.js b/ee/app/assets/javascripts/usage_quotas/group_view_metadata.js index 51785752c34ee9616fe1e4183754f1e2fc5df038..0d89c3d06838af97ed385abe62372029fd501a8e 100644 --- a/ee/app/assets/javascripts/usage_quotas/group_view_metadata.js +++ b/ee/app/assets/javascripts/usage_quotas/group_view_metadata.js @@ -5,18 +5,26 @@ import PipelineUsageApp from './pipelines/components/app.vue'; import { parseProvideData as parseStorageTabProvideData } from './storage/utils'; import { parseProvideData as parsePipelinesTabProvideData } from './pipelines/utils'; import { PIPELINES_TAB_METADATA_EL_SELECTOR } from './constants'; +import { getSeatTabMetadata } from './seats/tab_metadata'; export const usageQuotasViewProvideData = { ...parseStorageTabProvideData(document.querySelector(STORAGE_TAB_METADATA_EL_SELECTOR)), ...parsePipelinesTabProvideData(document.querySelector(PIPELINES_TAB_METADATA_EL_SELECTOR)), }; -const pipelineTabMetadata = { - title: __('Pipelines'), - component: PipelineUsageApp, - shouldRender: () => document.querySelector(PIPELINES_TAB_METADATA_EL_SELECTOR), +const getPipelineTabMetadata = () => { + const el = document.querySelector(PIPELINES_TAB_METADATA_EL_SELECTOR); + + if (!el) return false; + + return { + title: __('Pipelines'), + component: PipelineUsageApp, + }; }; -export const usageQuotasTabsMetadata = [pipelineTabMetadata, storageTabMetadata].filter((tab) => - tab.shouldRender(), -); +export const usageQuotasTabsMetadata = [ + getSeatTabMetadata(), + getPipelineTabMetadata(), + storageTabMetadata, +]; diff --git a/ee/app/assets/javascripts/usage_quotas/seats/index.js b/ee/app/assets/javascripts/usage_quotas/seats/index.js index a9898f4ebac60de432c677619d2a1519eab036e4..c54faf69a26c7d69a7ad085bd7602d3d6d65cd9a 100644 --- a/ee/app/assets/javascripts/usage_quotas/seats/index.js +++ b/ee/app/assets/javascripts/usage_quotas/seats/index.js @@ -2,61 +2,15 @@ import Vue from 'vue'; // eslint-disable-next-line no-restricted-imports import Vuex from 'vuex'; import VueApollo from 'vue-apollo'; -import { parseBoolean } from '~/lib/utils/common_utils'; -import SubscriptionSeats from 'ee/usage_quotas/seats/components/subscription_seats.vue'; -import apolloProvider from 'ee/usage_quotas/shared/provider'; -import { writeDataToApolloCache } from 'ee/usage_quotas/seats/graphql/utils'; -import initialStore from './store'; +import { getSeatTabMetadata } from './tab_metadata'; Vue.use(Vuex); Vue.use(VueApollo); -export default (containerId = 'js-seat-usage-app') => { - const el = document.getElementById(containerId); +export default () => { + const seatTabMetadata = getSeatTabMetadata(true); - if (!el) { - return false; - } + if (!seatTabMetadata) return false; - const { - fullPath, - namespaceId, - namespaceName, - isPublicNamespace, - seatUsageExportPath, - addSeatsHref, - hasNoSubscription, - maxFreeNamespaceSeats, - explorePlansPath, - enforcementFreeUserCapEnabled, - } = el.dataset; - - const store = new Vuex.Store( - initialStore({ - namespaceId, - namespaceName, - seatUsageExportPath, - addSeatsHref, - hasNoSubscription: parseBoolean(hasNoSubscription), - maxFreeNamespaceSeats: parseInt(maxFreeNamespaceSeats, 10), - explorePlansPath, - enforcementFreeUserCapEnabled: parseBoolean(enforcementFreeUserCapEnabled), - }), - ); - - return new Vue({ - el, - name: 'SeatsUsageApp', - apolloProvider: writeDataToApolloCache(apolloProvider, { subscriptionId: namespaceId }), - provide: { - explorePlansPath, - fullPath, - isPublicNamespace: parseBoolean(isPublicNamespace), - namespaceId, - }, - store, - render(createElement) { - return createElement(SubscriptionSeats); - }, - }); + return new Vue(seatTabMetadata.component); }; diff --git a/ee/app/assets/javascripts/usage_quotas/seats/tab_metadata.js b/ee/app/assets/javascripts/usage_quotas/seats/tab_metadata.js new file mode 100644 index 0000000000000000000000000000000000000000..1f4d0433341cd01ca8fd70b499404a356bc0e3fb --- /dev/null +++ b/ee/app/assets/javascripts/usage_quotas/seats/tab_metadata.js @@ -0,0 +1,93 @@ +// eslint-disable-next-line no-restricted-imports +import Vuex from 'vuex'; +import { __ } from '~/locale'; +import { parseBoolean } from '~/lib/utils/common_utils'; +import apolloProvider from '../shared/provider'; +import { SEATS_TAB_METADATA_EL_SELECTOR } from '../constants'; +import { writeDataToApolloCache as writeSeatsDataToApolloCache } from './graphql/utils'; +import initialSeatUsageStore from './store'; +import SeatUsageApp from './components/subscription_seats.vue'; + +export const parseProvideData = (el) => { + const { + fullPath, + namespaceId, + namespaceName, + isPublicNamespace, + seatUsageExportPath, + addSeatsHref, + hasNoSubscription, + maxFreeNamespaceSeats, + explorePlansPath, + enforcementFreeUserCapEnabled, + } = el.dataset; + + return { + fullPath, + namespaceId, + namespaceName, + isPublicNamespace: parseBoolean(isPublicNamespace), + seatUsageExportPath, + addSeatsHref, + hasNoSubscription: parseBoolean(hasNoSubscription), + maxFreeNamespaceSeats: parseInt(maxFreeNamespaceSeats, 10), + explorePlansPath, + enforcementFreeUserCapEnabled: parseBoolean(enforcementFreeUserCapEnabled), + }; +}; + +export const getSeatTabMetadata = (withMountElement = false) => { + const el = document.querySelector(SEATS_TAB_METADATA_EL_SELECTOR); + + if (!el) return false; + + const { + fullPath, + namespaceId, + namespaceName, + isPublicNamespace, + seatUsageExportPath, + addSeatsHref, + hasNoSubscription, + maxFreeNamespaceSeats, + explorePlansPath, + enforcementFreeUserCapEnabled, + } = parseProvideData(el); + + const store = new Vuex.Store( + initialSeatUsageStore({ + namespaceId, + namespaceName, + seatUsageExportPath, + addSeatsHref, + hasNoSubscription, + maxFreeNamespaceSeats, + explorePlansPath, + enforcementFreeUserCapEnabled, + }), + ); + + const seatTabMetadata = { + title: __('Seats'), + component: { + name: 'SeatUsageTab', + apolloProvider: writeSeatsDataToApolloCache(apolloProvider, { subscriptionId: namespaceId }), + provide: { + explorePlansPath, + fullPath, + isPublicNamespace, + namespaceId, + }, + store, + render(createElement) { + return createElement(SeatUsageApp); + }, + }, + }; + + if (withMountElement) { + seatTabMetadata.component.el = el; + } + + return seatTabMetadata; +}; diff --git a/ee/app/views/groups/usage_quotas/index.html.haml b/ee/app/views/groups/usage_quotas/index.html.haml index de463468ee0dba2fe441b57aa70f1a78e4e2d390..921ea1fc0eece978658e3f827e6da55fc859c982 100644 --- a/ee/app/views/groups/usage_quotas/index.html.haml +++ b/ee/app/views/groups/usage_quotas/index.html.haml @@ -7,7 +7,16 @@ - @force_desktop_expanded_sidebar = true - if Feature.enabled?(:usage_quotas_for_all_editions, @group) + = render Namespaces::FreeUserCap::UsageQuotaAlertComponent.new(namespace: @group.root_ancestor, + user: current_user, + content_class: 'gl-my-3') + + = render Namespaces::FreeUserCap::UsageQuotaTrialAlertComponent.new(namespace: @group.root_ancestor, + user: current_user, + content_class: 'gl-my-3') + #js-usage-quotas-view{ data: { namespace_name: @group.name } } + #js-seat-usage-app{ data: group_seats_usage_quota_app_data(@group) } #js-namespace-storage-app{ data: storage_usage_app_data(@group) } - if can? current_user, :admin_ci_minutes, @group #js-pipeline-usage-app{ data: pipeline_usage_app_data(@group) }