Commit 6c9d9461 authored by Mayra Cabrera's avatar Mayra Cabrera

Merge branch 'led/334861-move-seat-usage-page-from-billing-to-usage-quotas' into 'master'

Move `seats usage` from billing to usage quotas

See merge request gitlab-org/gitlab!67576
parents fc4a9c46 90d412aa
...@@ -94,6 +94,8 @@ The following table describes details of your subscription: ...@@ -94,6 +94,8 @@ The following table describes details of your subscription:
To view a list of seats being used, go to **Settings > Billing**. To view a list of seats being used, go to **Settings > Billing**.
Under **Seats currently in use**, select **See usage**. Under **Seats currently in use**, select **See usage**.
You can also see this information in your group settings by going to **Menu > Groups > Your Group > Settings > Usage Quotas**, and the information about **Seat usage** will be under the **Seats** tab.
The **Seat usage** page lists all users occupying seats. Details for each user include: The **Seat usage** page lists all users occupying seats. Details for each user include:
- Full name - Full name
......
...@@ -2,11 +2,6 @@ export const TABLE_TYPE_DEFAULT = 'default'; ...@@ -2,11 +2,6 @@ export const TABLE_TYPE_DEFAULT = 'default';
export const TABLE_TYPE_FREE = 'free'; export const TABLE_TYPE_FREE = 'free';
export const TABLE_TYPE_TRIAL = 'trial'; export const TABLE_TYPE_TRIAL = 'trial';
// Billable Seats HTTP headers
export const HEADER_TOTAL_ENTRIES = 'x-total';
export const HEADER_PAGE_NUMBER = 'x-page';
export const HEADER_ITEMS_PER_PAGE = 'x-per-page';
export const DAYS_FOR_RENEWAL = 15; export const DAYS_FOR_RENEWAL = 15;
export const PLAN_TITLE_TRIAL_TEXT = ' Trial'; export const PLAN_TITLE_TRIAL_TEXT = ' Trial';
import initSeatUsage from 'ee/billings/seat_usage';
initSeatUsage();
import otherStorageCounter from 'ee/other_storage_counter'; import otherStorageCounter from 'ee/other_storage_counter';
import SeatUsageApp from 'ee/seat_usage';
import storageCounter from 'ee/storage_counter'; import storageCounter from 'ee/storage_counter';
import LinkedTabs from '~/lib/utils/bootstrap_linked_tabs'; import LinkedTabs from '~/lib/utils/bootstrap_linked_tabs';
import initSearchSettings from '~/search_settings'; import initSearchSettings from '~/search_settings';
if (document.querySelector('#js-storage-counter-app')) { const initLinkedTabs = () => {
storageCounter(); if (!document.querySelector('.js-storage-tabs')) {
return false;
}
// eslint-disable-next-line no-new return new LinkedTabs({
new LinkedTabs({ defaultAction: '#seats-quota-tab',
defaultAction: '#pipelines-quota-tab',
parentEl: '.js-storage-tabs', parentEl: '.js-storage-tabs',
hashedTabs: true, hashedTabs: true,
}); });
} };
if (document.querySelector('#js-other-storage-counter-app')) { const initVueApps = () => {
otherStorageCounter(); if (document.querySelector('#js-seat-usage-app')) {
SeatUsageApp();
}
// eslint-disable-next-line no-new if (document.querySelector('#js-storage-counter-app')) {
new LinkedTabs({ storageCounter();
defaultAction: '#pipelines-quota-tab', }
parentEl: '.js-other-storage-tabs',
hashedTabs: true, if (document.querySelector('#js-other-storage-counter-app')) {
}); otherStorageCounter();
} }
};
initVueApps();
initLinkedTabs();
initSearchSettings(); initSearchSettings();
...@@ -10,7 +10,7 @@ import { mapActions, mapState } from 'vuex'; ...@@ -10,7 +10,7 @@ import { mapActions, mapState } from 'vuex';
import { import {
REMOVE_BILLABLE_MEMBER_MODAL_ID, REMOVE_BILLABLE_MEMBER_MODAL_ID,
REMOVE_BILLABLE_MEMBER_MODAL_CONTENT_TEXT_TEMPLATE, REMOVE_BILLABLE_MEMBER_MODAL_CONTENT_TEXT_TEMPLATE,
} from 'ee/billings/seat_usage/constants'; } from 'ee/seat_usage/constants';
import csrf from '~/lib/utils/csrf'; import csrf from '~/lib/utils/csrf';
import { __, s__, sprintf } from '~/locale'; import { __, s__, sprintf } from '~/locale';
......
...@@ -22,7 +22,7 @@ import { ...@@ -22,7 +22,7 @@ import {
CANNOT_REMOVE_BILLABLE_MEMBER_MODAL_TITLE, CANNOT_REMOVE_BILLABLE_MEMBER_MODAL_TITLE,
CANNOT_REMOVE_BILLABLE_MEMBER_MODAL_CONTENT, CANNOT_REMOVE_BILLABLE_MEMBER_MODAL_CONTENT,
SORT_OPTIONS, SORT_OPTIONS,
} from 'ee/billings/seat_usage/constants'; } from 'ee/seat_usage/constants';
import { s__, __ } from '~/locale'; import { s__, __ } from '~/locale';
import FilterSortContainerRoot from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue'; import FilterSortContainerRoot from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
import RemoveBillableMemberModal from './remove_billable_member_modal.vue'; import RemoveBillableMemberModal from './remove_billable_member_modal.vue';
...@@ -99,9 +99,6 @@ export default { ...@@ -99,9 +99,6 @@ export default {
}, ''); }, '');
this.setSearchQuery(searchQuery.trim() || null); this.setSearchQuery(searchQuery.trim() || null);
}, },
handleSortOptionChange(sortOption) {
this.setSortOption(sortOption);
},
displayRemoveMemberModal(user) { displayRemoveMemberModal(user) {
if (user.removable) { if (user.removable) {
this.setBillableMemberToRemove(user); this.setBillableMemberToRemove(user);
...@@ -161,7 +158,7 @@ export default { ...@@ -161,7 +158,7 @@ export default {
:sort-options="$options.sortOptions" :sort-options="$options.sortOptions"
initial-sort-by="last_activity_on_desc" initial-sort-by="last_activity_on_desc"
@onFilter="applyFilter" @onFilter="applyFilter"
@onSort="handleSortOptionChange" @onSort="setSortOption"
/> />
</div> </div>
......
...@@ -2,6 +2,11 @@ import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants'; ...@@ -2,6 +2,11 @@ import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants';
import { thWidthClass } from '~/lib/utils/table_utility'; import { thWidthClass } from '~/lib/utils/table_utility';
import { __, s__ } from '~/locale'; import { __, s__ } from '~/locale';
// Billable Seats HTTP headers
export const HEADER_TOTAL_ENTRIES = 'x-total';
export const HEADER_PAGE_NUMBER = 'x-page';
export const HEADER_ITEMS_PER_PAGE = 'x-per-page';
export const FIELDS = [ export const FIELDS = [
{ {
key: 'user', key: 'user',
......
...@@ -5,17 +5,18 @@ import initialStore from './store'; ...@@ -5,17 +5,18 @@ import initialStore from './store';
Vue.use(Vuex); Vue.use(Vuex);
export default (containerId = 'js-seat-usage') => { export default (containerId = 'js-seat-usage-app') => {
const containerEl = document.getElementById(containerId); const el = document.getElementById(containerId);
if (!containerEl) { if (!el) {
return false; return false;
} }
const { namespaceId, namespaceName, seatUsageExportPath } = containerEl.dataset; const { namespaceId, namespaceName, seatUsageExportPath } = el.dataset;
return new Vue({ return new Vue({
el: containerEl, el,
apolloProvider: {},
store: new Vuex.Store(initialStore({ namespaceId, namespaceName, seatUsageExportPath })), store: new Vuex.Store(initialStore({ namespaceId, namespaceName, seatUsageExportPath })),
render(createElement) { render(createElement) {
return createElement(SubscriptionSeats); return createElement(SubscriptionSeats);
......
...@@ -3,7 +3,7 @@ import { ...@@ -3,7 +3,7 @@ import {
HEADER_TOTAL_ENTRIES, HEADER_TOTAL_ENTRIES,
HEADER_PAGE_NUMBER, HEADER_PAGE_NUMBER,
HEADER_ITEMS_PER_PAGE, HEADER_ITEMS_PER_PAGE,
} from 'ee/billings/constants'; } from 'ee/seat_usage/constants';
import * as types from './mutation_types'; import * as types from './mutation_types';
export default { export default {
......
...@@ -12,6 +12,7 @@ class Groups::SeatUsageController < Groups::ApplicationController ...@@ -12,6 +12,7 @@ class Groups::SeatUsageController < Groups::ApplicationController
def show def show
respond_to do |format| respond_to do |format|
format.html do format.html do
redirect_to_seat_usage_tab
end end
format.csv do format.csv do
...@@ -24,7 +25,7 @@ class Groups::SeatUsageController < Groups::ApplicationController ...@@ -24,7 +25,7 @@ class Groups::SeatUsageController < Groups::ApplicationController
else else
flash[:alert] = _('Failed to generate export, please try again later.') flash[:alert] = _('Failed to generate export, please try again later.')
redirect_to group_seat_usage_path(group) redirect_to_seat_usage_tab
end end
end end
end end
...@@ -32,6 +33,10 @@ class Groups::SeatUsageController < Groups::ApplicationController ...@@ -32,6 +33,10 @@ class Groups::SeatUsageController < Groups::ApplicationController
private private
def redirect_to_seat_usage_tab
redirect_to group_usage_quotas_path(group, anchor: 'seats-quota-tab')
end
def csv_filename def csv_filename
"seat-usage-export-#{Time.current.to_s(:number)}.csv" "seat-usage-export-#{Time.current.to_s(:number)}.csv"
end end
......
...@@ -165,7 +165,7 @@ module BillingPlansHelper ...@@ -165,7 +165,7 @@ module BillingPlansHelper
def billable_seats_href(namespace) def billable_seats_href(namespace)
return unless namespace.group? return unless namespace.group?
group_seat_usage_path(namespace) group_usage_quotas_path(namespace, anchor: 'seats-quota-tab')
end end
def offer_from_previous_tier?(namespace_id, plan_id) def offer_from_previous_tier?(namespace_id, plan_id)
......
- page_title s_('SeatUsage|Seat usage')
- add_to_breadcrumbs _('Billing'), group_billings_path(@group)
#js-seat-usage{ data: { namespace_id: @group.id, namespace_name: @group.name, seat_usage_export_path: group_seat_usage_path(@group, format: :csv) } }
...@@ -15,7 +15,10 @@ ...@@ -15,7 +15,10 @@
.top-area.scrolling-tabs-container.inner-page-scroll-tabs .top-area.scrolling-tabs-container.inner-page-scroll-tabs
%ul.nav.nav-tabs.nav-links.scrolling-tabs.separator.js-storage-tabs{ role: 'tablist' } %ul.nav.nav-tabs.nav-links.scrolling-tabs.separator.js-storage-tabs{ role: 'tablist' }
%li.nav-item %li.nav-item
%a.nav-link#pipelines-quota{ data: { toggle: "tab", action: '#pipelines-quota-tab' }, href: '#pipelines-quota-tab', 'aria-controls': '#pipelines-quota-tab', 'aria-selected': true } %a.nav-link#seats-quota{ data: { toggle: "tab", action: '#seats-quota-tab' }, href: '#seats-quota-tab', 'aria-controls': '#seats-quota-tab', 'aria-selected': true }
= s_('UsageQuota|Seats')
%li.nav-item
%a.nav-link#pipelines-quota{ data: { toggle: "tab", action: '#pipelines-quota-tab' }, href: '#pipelines-quota-tab', 'aria-controls': '#pipelines-quota-tab', 'aria-selected': false }
= s_('UsageQuota|Pipelines') = s_('UsageQuota|Pipelines')
%li.nav-item %li.nav-item
%a.nav-link#storage-quota{ data: { toggle: "tab", action: '#storage-quota-tab' }, href: '#storage-quota-tab', 'aria-controls': '#storage-quota-tab', 'aria-selected': false } %a.nav-link#storage-quota{ data: { toggle: "tab", action: '#storage-quota-tab' }, href: '#storage-quota-tab', 'aria-controls': '#storage-quota-tab', 'aria-selected': false }
...@@ -25,6 +28,8 @@ ...@@ -25,6 +28,8 @@
%a.nav-link#storage-quota{ data: { toggle: "tab", action: '#other-storage-quota-tab' }, href: '#other-storage-quota-tab', 'aria-controls': '#other-storage-quota-tab', 'aria-selected': false } %a.nav-link#storage-quota{ data: { toggle: "tab", action: '#other-storage-quota-tab' }, href: '#other-storage-quota-tab', 'aria-controls': '#other-storage-quota-tab', 'aria-selected': false }
= s_('UsageQuota|Other Storage') = s_('UsageQuota|Other Storage')
.tab-content .tab-content
.tab-pane#seats-quota-tab
#js-seat-usage-app{ data: { namespace_id: @group.id, namespace_name: @group.name, seat_usage_export_path: group_seat_usage_path(@group, format: :csv) } }
.tab-pane#pipelines-quota-tab .tab-pane#pipelines-quota-tab
= render "namespaces/pipelines_quota/list", = render "namespaces/pipelines_quota/list",
locals: { namespace: @group, projects: @projects } locals: { namespace: @group, projects: @projects }
......
...@@ -24,11 +24,10 @@ RSpec.describe Groups::SeatUsageController do ...@@ -24,11 +24,10 @@ RSpec.describe Groups::SeatUsageController do
end end
context 'when html format' do context 'when html format' do
it 'renders show with 200 status code' do it 'redirects to /groups/%{group_id}/-/seat_usage' do
get_show get_show
expect(response).to have_gitlab_http_status(:ok) expect(response).to redirect_to(group_usage_quotas_path(group, anchor: 'seats-quota-tab'))
expect(response).to render_template(:show)
end end
it 'responds with 404 Not Found if the group is not top-level group' do it 'responds with 404 Not Found if the group is not top-level group' do
...@@ -81,7 +80,7 @@ RSpec.describe Groups::SeatUsageController do ...@@ -81,7 +80,7 @@ RSpec.describe Groups::SeatUsageController do
get_show(format: :csv) get_show(format: :csv)
expect(flash[:alert]).to eq 'Failed to generate export, please try again later.' expect(flash[:alert]).to eq 'Failed to generate export, please try again later.'
expect(response).to redirect_to(group_seat_usage_path(group)) expect(response).to redirect_to(group_usage_quotas_path(group, anchor: 'seats-quota-tab'))
end end
end end
end end
......
...@@ -88,7 +88,7 @@ RSpec.describe 'Groups > Billing', :js do ...@@ -88,7 +88,7 @@ RSpec.describe 'Groups > Billing', :js do
expect(page).to have_link("Manage", href: "#{EE::SUBSCRIPTIONS_URL}/subscriptions") expect(page).to have_link("Manage", href: "#{EE::SUBSCRIPTIONS_URL}/subscriptions")
expect(page).to have_link("Add seats", href: extra_seats_url) expect(page).to have_link("Add seats", href: extra_seats_url)
expect(page).to have_link("Renew", href: renew_url) expect(page).to have_link("Renew", href: renew_url)
expect(page).to have_link("See usage", href: group_seat_usage_path(group)) expect(page).to have_link("See usage", href: group_usage_quotas_path(group, anchor: 'seats-quota-tab'))
end end
end end
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe 'Groups > Billing > Seat Usage', :js do RSpec.describe 'Groups > Usage Quotas > Seat Usage', :js do
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) } let_it_be(:group) { create(:group) }
let_it_be(:sub_group) { create(:group, parent: group) } let_it_be(:sub_group) { create(:group, parent: group) }
......
import {
HEADER_TOTAL_ENTRIES,
HEADER_PAGE_NUMBER,
HEADER_ITEMS_PER_PAGE,
} from 'ee/billings/constants';
export const mockDataSubscription = { export const mockDataSubscription = {
gold: { gold: {
plan: { plan: {
...@@ -64,123 +58,3 @@ export const mockDataSubscription = { ...@@ -64,123 +58,3 @@ export const mockDataSubscription = {
}, },
}, },
}; };
export const mockDataSeats = {
data: [
{
id: 2,
name: 'Administrator',
username: 'root',
avatar_url: 'path/to/img_administrator',
web_url: 'path/to/administrator',
email: 'administrator@email.com',
last_activity_on: '2020-03-01',
membership_type: 'group_member',
removable: true,
},
{
id: 3,
name: 'Agustin Walker',
username: 'lester.orn',
avatar_url: 'path/to/img_agustin_walker',
web_url: 'path/to/agustin_walker',
email: 'agustin_walker@email.com',
last_activity_on: '2020-03-01',
membership_type: 'group_member',
removable: true,
},
{
id: 4,
name: 'Joella Miller',
username: 'era',
avatar_url: 'path/to/img_joella_miller',
web_url: 'path/to/joella_miller',
last_activity_on: null,
email: null,
membership_type: 'group_invite',
removable: false,
},
{
id: 5,
name: 'John Doe',
username: 'jdoe',
avatar_url: 'path/to/img_john_doe',
web_url: 'path/to/john_doe',
last_activity_on: null,
email: 'jdoe@email.com',
membership_type: 'project_invite',
removable: false,
},
],
headers: {
[HEADER_TOTAL_ENTRIES]: '3',
[HEADER_PAGE_NUMBER]: '1',
[HEADER_ITEMS_PER_PAGE]: '1',
},
};
export const mockMemberDetails = [
{
id: 173,
source_id: 155,
source_full_name: 'group_with_ultimate_plan / subgroup',
created_at: '2021-02-25T08:21:32.257Z',
expires_at: null,
access_level: { string_value: 'Owner', integer_value: 50 },
},
];
export const mockTableItems = [
{
email: 'administrator@email.com',
user: {
id: 2,
avatar_url: 'path/to/img_administrator',
name: 'Administrator',
username: '@root',
web_url: 'path/to/administrator',
last_activity_on: '2020-03-01',
membership_type: 'group_member',
removable: true,
},
},
{
email: 'agustin_walker@email.com',
user: {
id: 3,
avatar_url: 'path/to/img_agustin_walker',
name: 'Agustin Walker',
username: '@lester.orn',
web_url: 'path/to/agustin_walker',
last_activity_on: '2020-03-01',
membership_type: 'group_member',
removable: true,
},
},
{
email: null,
user: {
id: 4,
avatar_url: 'path/to/img_joella_miller',
name: 'Joella Miller',
username: '@era',
web_url: 'path/to/joella_miller',
last_activity_on: null,
membership_type: 'group_invite',
removable: false,
},
},
{
email: 'jdoe@email.com',
user: {
id: 5,
avatar_url: 'path/to/img_john_doe',
name: 'John Doe',
username: '@jdoe',
web_url: 'path/to/john_doe',
last_activity_on: null,
membership_type: 'project_invite',
removable: false,
},
},
];
...@@ -2,7 +2,7 @@ import { GlSprintf } from '@gitlab/ui'; ...@@ -2,7 +2,7 @@ import { GlSprintf } from '@gitlab/ui';
import { shallowMount, createLocalVue } from '@vue/test-utils'; import { shallowMount, createLocalVue } from '@vue/test-utils';
import { nextTick } from 'vue'; import { nextTick } from 'vue';
import Vuex from 'vuex'; import Vuex from 'vuex';
import RemoveBillableMemberModal from 'ee/billings/seat_usage/components/remove_billable_member_modal.vue'; import RemoveBillableMemberModal from 'ee/seat_usage/components/remove_billable_member_modal.vue';
const localVue = createLocalVue(); const localVue = createLocalVue();
localVue.use(Vuex); localVue.use(Vuex);
......
...@@ -2,11 +2,11 @@ import { GlTable } from '@gitlab/ui'; ...@@ -2,11 +2,11 @@ import { GlTable } from '@gitlab/ui';
import { shallowMount, createLocalVue } from '@vue/test-utils'; import { shallowMount, createLocalVue } from '@vue/test-utils';
import Vuex from 'vuex'; import Vuex from 'vuex';
import Api from 'ee/api'; import Api from 'ee/api';
import SubscriptionSeatDetails from 'ee/billings/seat_usage/components/subscription_seat_details.vue'; import SubscriptionSeatDetails from 'ee/seat_usage/components/subscription_seat_details.vue';
import SubscriptionSeatDetailsLoader from 'ee/billings/seat_usage/components/subscription_seat_details_loader.vue'; import SubscriptionSeatDetailsLoader from 'ee/seat_usage/components/subscription_seat_details_loader.vue';
import createStore from 'ee/billings/seat_usage/store'; import createStore from 'ee/seat_usage/store';
import initState from 'ee/billings/seat_usage/store/state'; import initState from 'ee/seat_usage/store/state';
import { mockMemberDetails } from 'ee_jest/billings/mock_data'; import { mockMemberDetails } from 'ee_jest/seat_usage/mock_data';
import { stubComponent } from 'helpers/stub_component'; import { stubComponent } from 'helpers/stub_component';
const localVue = createLocalVue(); const localVue = createLocalVue();
......
...@@ -9,9 +9,9 @@ import { ...@@ -9,9 +9,9 @@ import {
} from '@gitlab/ui'; } from '@gitlab/ui';
import { mount, shallowMount, createLocalVue } from '@vue/test-utils'; import { mount, shallowMount, createLocalVue } from '@vue/test-utils';
import Vuex from 'vuex'; import Vuex from 'vuex';
import SubscriptionSeats from 'ee/billings/seat_usage/components/subscription_seats.vue'; import SubscriptionSeats from 'ee/seat_usage/components/subscription_seats.vue';
import { CANNOT_REMOVE_BILLABLE_MEMBER_MODAL_CONTENT } from 'ee/billings/seat_usage/constants'; import { CANNOT_REMOVE_BILLABLE_MEMBER_MODAL_CONTENT } from 'ee/seat_usage/constants';
import { mockDataSeats, mockTableItems } from 'ee_jest/billings/mock_data'; import { mockDataSeats, mockTableItems } from 'ee_jest/seat_usage/mock_data';
import { extendedWrapper } from 'helpers/vue_test_utils_helper'; import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import FilterSortContainerRoot from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue'; import FilterSortContainerRoot from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
......
import {
HEADER_TOTAL_ENTRIES,
HEADER_PAGE_NUMBER,
HEADER_ITEMS_PER_PAGE,
} from 'ee/seat_usage/constants';
export const mockDataSeats = {
data: [
{
id: 2,
name: 'Administrator',
username: 'root',
avatar_url: 'path/to/img_administrator',
web_url: 'path/to/administrator',
email: 'administrator@email.com',
last_activity_on: '2020-03-01',
membership_type: 'group_member',
removable: true,
},
{
id: 3,
name: 'Agustin Walker',
username: 'lester.orn',
avatar_url: 'path/to/img_agustin_walker',
web_url: 'path/to/agustin_walker',
email: 'agustin_walker@email.com',
last_activity_on: '2020-03-01',
membership_type: 'group_member',
removable: true,
},
{
id: 4,
name: 'Joella Miller',
username: 'era',
avatar_url: 'path/to/img_joella_miller',
web_url: 'path/to/joella_miller',
last_activity_on: null,
email: null,
membership_type: 'group_invite',
removable: false,
},
{
id: 5,
name: 'John Doe',
username: 'jdoe',
avatar_url: 'path/to/img_john_doe',
web_url: 'path/to/john_doe',
last_activity_on: null,
email: 'jdoe@email.com',
membership_type: 'project_invite',
removable: false,
},
],
headers: {
[HEADER_TOTAL_ENTRIES]: '3',
[HEADER_PAGE_NUMBER]: '1',
[HEADER_ITEMS_PER_PAGE]: '1',
},
};
export const mockMemberDetails = [
{
id: 173,
source_id: 155,
source_full_name: 'group_with_ultimate_plan / subgroup',
created_at: '2021-02-25T08:21:32.257Z',
expires_at: null,
access_level: { string_value: 'Owner', integer_value: 50 },
},
];
export const mockTableItems = [
{
email: 'administrator@email.com',
user: {
id: 2,
avatar_url: 'path/to/img_administrator',
name: 'Administrator',
username: '@root',
web_url: 'path/to/administrator',
last_activity_on: '2020-03-01',
membership_type: 'group_member',
removable: true,
},
},
{
email: 'agustin_walker@email.com',
user: {
id: 3,
avatar_url: 'path/to/img_agustin_walker',
name: 'Agustin Walker',
username: '@lester.orn',
web_url: 'path/to/agustin_walker',
last_activity_on: '2020-03-01',
membership_type: 'group_member',
removable: true,
},
},
{
email: null,
user: {
id: 4,
avatar_url: 'path/to/img_joella_miller',
name: 'Joella Miller',
username: '@era',
web_url: 'path/to/joella_miller',
last_activity_on: null,
membership_type: 'group_invite',
removable: false,
},
},
{
email: 'jdoe@email.com',
user: {
id: 5,
avatar_url: 'path/to/img_john_doe',
name: 'John Doe',
username: '@jdoe',
web_url: 'path/to/john_doe',
last_activity_on: null,
membership_type: 'project_invite',
removable: false,
},
},
];
import MockAdapter from 'axios-mock-adapter'; import MockAdapter from 'axios-mock-adapter';
import * as GroupsApi from 'ee/api/groups_api'; import * as GroupsApi from 'ee/api/groups_api';
import * as actions from 'ee/billings/seat_usage/store/actions'; import * as actions from 'ee/seat_usage/store/actions';
import * as types from 'ee/billings/seat_usage/store/mutation_types'; import * as types from 'ee/seat_usage/store/mutation_types';
import State from 'ee/billings/seat_usage/store/state'; import State from 'ee/seat_usage/store/state';
import { mockDataSeats, mockMemberDetails } from 'ee_jest/billings/mock_data'; import { mockDataSeats, mockMemberDetails } from 'ee_jest/seat_usage/mock_data';
import testAction from 'helpers/vuex_action_helper'; import testAction from 'helpers/vuex_action_helper';
import createFlash, { FLASH_TYPES } from '~/flash'; import createFlash, { FLASH_TYPES } from '~/flash';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
......
import * as getters from 'ee/billings/seat_usage/store/getters'; import * as getters from 'ee/seat_usage/store/getters';
import State from 'ee/billings/seat_usage/store/state'; import State from 'ee/seat_usage/store/state';
import { mockDataSeats, mockTableItems, mockMemberDetails } from 'ee_jest/billings/mock_data'; import { mockDataSeats, mockTableItems, mockMemberDetails } from 'ee_jest/seat_usage/mock_data';
describe('Seat usage table getters', () => { describe('Seat usage table getters', () => {
let state; let state;
......
import * as types from 'ee/billings/seat_usage/store/mutation_types'; import * as types from 'ee/seat_usage/store/mutation_types';
import mutations from 'ee/billings/seat_usage/store/mutations'; import mutations from 'ee/seat_usage/store/mutations';
import createState from 'ee/billings/seat_usage/store/state'; import createState from 'ee/seat_usage/store/state';
import { mockDataSeats, mockMemberDetails } from 'ee_jest/billings/mock_data'; import { mockDataSeats, mockMemberDetails } from 'ee_jest/seat_usage/mock_data';
describe('EE billings seats module mutations', () => { describe('EE seats module mutations', () => {
let state; let state;
beforeEach(() => { beforeEach(() => {
......
...@@ -10,7 +10,7 @@ RSpec.describe BillingPlansHelper do ...@@ -10,7 +10,7 @@ RSpec.describe BillingPlansHelper do
let(:customer_portal_url) { "#{EE::SUBSCRIPTIONS_URL}/subscriptions" } let(:customer_portal_url) { "#{EE::SUBSCRIPTIONS_URL}/subscriptions" }
let(:add_seats_href) { "#{EE::SUBSCRIPTIONS_URL}/gitlab/namespaces/#{group.id}/extra_seats" } let(:add_seats_href) { "#{EE::SUBSCRIPTIONS_URL}/gitlab/namespaces/#{group.id}/extra_seats" }
let(:plan_renew_href) { "#{EE::SUBSCRIPTIONS_URL}/gitlab/namespaces/#{group.id}/renew" } let(:plan_renew_href) { "#{EE::SUBSCRIPTIONS_URL}/gitlab/namespaces/#{group.id}/renew" }
let(:billable_seats_href) { helper.group_seat_usage_path(group) } let(:billable_seats_href) { helper.group_usage_quotas_path(group, anchor: 'seats-quota-tab') }
let(:refresh_seats_href) { helper.refresh_seats_group_billings_url(group) } let(:refresh_seats_href) { helper.refresh_seats_group_billings_url(group) }
let(:plan) do let(:plan) do
...@@ -132,7 +132,7 @@ RSpec.describe BillingPlansHelper do ...@@ -132,7 +132,7 @@ RSpec.describe BillingPlansHelper do
let(:namespace) { build(:namespace) } let(:namespace) { build(:namespace) }
it 'does not return billable_seats_href' do it 'does not return billable_seats_href' do
expect(subject).not_to include(billable_seats_href: helper.group_seat_usage_path(namespace)) expect(subject).not_to include(billable_seats_href: helper.group_usage_quotas_path(namespace, anchor: 'seats-quota-tab'))
end end
end end
...@@ -140,7 +140,7 @@ RSpec.describe BillingPlansHelper do ...@@ -140,7 +140,7 @@ RSpec.describe BillingPlansHelper do
let(:namespace) { build(:group) } let(:namespace) { build(:group) }
it 'returns billable_seats_href for group' do it 'returns billable_seats_href for group' do
expect(subject).to include(billable_seats_href: helper.group_seat_usage_path(namespace)) expect(subject).to include(billable_seats_href: helper.group_usage_quotas_path(namespace, anchor: 'seats-quota-tab'))
end end
end end
end end
......
...@@ -29391,9 +29391,6 @@ msgstr[1] "" ...@@ -29391,9 +29391,6 @@ msgstr[1] ""
msgid "Searching by both author and message is currently not supported." msgid "Searching by both author and message is currently not supported."
msgstr "" msgstr ""
msgid "SeatUsage|Seat usage"
msgstr ""
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)" msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr "" msgstr ""
...@@ -35998,6 +35995,9 @@ msgstr "" ...@@ -35998,6 +35995,9 @@ msgstr ""
msgid "UsageQuota|Repository" msgid "UsageQuota|Repository"
msgstr "" msgstr ""
msgid "UsageQuota|Seats"
msgstr ""
msgid "UsageQuota|Snippets" msgid "UsageQuota|Snippets"
msgstr "" msgstr ""
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment