Commit e077b275 authored by Savas Vedova's avatar Savas Vedova

Merge branch 'ag-273028-subs-details-license-history' into 'master'

Subscription Details: License History Table

See merge request gitlab-org/gitlab!58660
parents 41586988 6bad5af3
<script>
import { GlBadge, GlTable } from '@gitlab/ui';
import { kebabCase } from 'lodash';
import { capitalizeFirstCharacter } from '~/lib/utils/text_utility';
import sprintf from '~/locale/sprintf';
import { detailsLabels, subscriptionTable, subscriptionTypeText } from '../constants';
const DEFAULT_BORDER_CLASSES = 'gl-border-b-1! gl-border-b-gray-100! gl-border-b-solid!';
const DEFAULT_TH_CLASSES = 'gl-bg-white! gl-border-t-0! gl-pb-5! gl-px-5! gl-text-gray-700!';
const DEFAULT_TD_CLASSES = 'gl-py-5!';
const tdAttr = (_, key) => ({ 'data-testid': `subscription-cell-${kebabCase(key)}` });
const tdClassBase = [DEFAULT_BORDER_CLASSES, DEFAULT_TD_CLASSES];
const tdClassHighlight = [...tdClassBase, 'gl-bg-blue-50!'];
const thClass = [DEFAULT_BORDER_CLASSES, DEFAULT_TH_CLASSES];
export default {
i18n: {
subscriptionHistoryTitle: subscriptionTable.title,
},
name: 'SubscriptionDetailsHistory',
components: {
GlBadge,
GlTable,
},
props: {
currentSubscriptionId: {
type: String,
required: false,
default: null,
},
subscriptionList: {
type: Array,
required: true,
},
},
data() {
return {
fields: [
{
key: 'name',
label: detailsLabels.name,
tdAttr,
tdClass: this.cellClass,
thClass,
},
{
key: 'email',
label: detailsLabels.email,
tdAttr,
tdClass: this.cellClass,
thClass,
},
{
key: 'company',
label: detailsLabels.company,
tdAttr,
tdClass: this.cellClass,
thClass,
},
{
key: 'plan',
label: detailsLabels.plan,
tdAttr,
tdClass: this.cellClass,
thClass,
},
{
key: 'startsAt',
label: subscriptionTable.activatedOn,
tdAttr,
tdClass: this.cellClass,
thClass,
},
{
key: 'validFrom',
label: subscriptionTable.validFrom,
tdAttr,
tdClass: this.cellClass,
thClass,
},
{
key: 'expiresAt',
label: subscriptionTable.expiresOn,
tdAttr,
tdClass: this.cellClass,
thClass,
},
{
key: 'usersInLicense',
label: subscriptionTable.seats,
tdAttr,
tdClass: this.cellClass,
thClass,
},
{
key: 'type',
label: subscriptionTable.type,
tdAttr,
tdClass: this.cellClass,
thClass,
},
],
};
},
methods: {
cellClass(_, x, item) {
return item.id === this.currentSubscriptionId ? tdClassHighlight : tdClassBase;
},
getType(type) {
return sprintf(subscriptionTypeText, { type: capitalizeFirstCharacter(type) });
},
rowAttr() {
return {
'data-testid': 'subscription-history-row',
};
},
rowClass(item) {
return item.id === this.currentSubscriptionId ? 'gl-font-weight-bold gl-text-blue-500' : '';
},
},
};
</script>
<template>
<section>
<header>
<h2 class="gl-mb-6 gl-mt-0">{{ $options.i18n.subscriptionHistoryTitle }}</h2>
</header>
<gl-table
:details-td-class="$options.tdClass"
:fields="fields"
:items="subscriptionList"
:tbody-tr-attr="rowAttr"
:tbody-tr-class="rowClass"
responsive
stacked="sm"
>
<template #cell(type)="{ item }">
<gl-badge size="md" variant="info">{{ getType(item.type) }}</gl-badge>
</template>
</gl-table>
</section>
</template>
import { __, s__ } from '~/locale'; import { __, s__ } from '~/locale';
export const subscriptionDetailsHeaderText = s__('CloudLicense|Subscription details'); export const subscriptionDetailsHeaderText = s__('SuperSonics|Subscription details');
export const licensedToHeaderText = s__('CloudLicense|Licensed to'); export const licensedToHeaderText = s__('SuperSonics|Licensed to');
export const manageSubscriptionButtonText = s__('CloudLicense|Manage'); export const manageSubscriptionButtonText = s__('SuperSonics|Manage');
export const syncSubscriptionButtonText = s__('CloudLicense|Sync Subscription details'); export const syncSubscriptionButtonText = s__('SuperSonics|Sync Subscription details');
export const copySubscriptionIdButtonText = __('Copy'); export const copySubscriptionIdButtonText = __('Copy');
export const subscriptionTypeText = __('%{type} License');
export const detailsLabels = { export const detailsLabels = {
address: __('Address'), address: __('Address'),
id: s__('CloudLicense|ID'), id: s__('SuperSonics|ID'),
company: __('Company'), company: __('Company'),
email: __('Email'), email: __('Email'),
lastSync: s__('CloudLicense|Last Sync'), lastSync: s__('SuperSonics|Last Sync'),
name: __('Name'), name: __('Name'),
plan: s__('CloudLicense|Plan'), plan: s__('SuperSonics|Plan'),
startsAt: s__('CloudLicense|Started'), renews: s__('SuperSonics|Renews'),
renews: s__('CloudLicense|Renews'), startsAt: s__('SuperSonics|Started'),
}; };
export const billableUsersTitle = s__('CloudLicense|Billable users'); export const billableUsersTitle = s__('CloudLicense|Billable users');
...@@ -33,3 +34,15 @@ export const usersInSubscriptionText = s__( ...@@ -33,3 +34,15 @@ export const usersInSubscriptionText = s__(
export const usersOverSubscriptionText = s__( export const usersOverSubscriptionText = s__(
`CloudLicense|You'll be charged for %{trueUpLinkStart}users over license%{trueUpLinkEnd} on a quarterly or annual basis, depending on the terms of your agreement.`, `CloudLicense|You'll be charged for %{trueUpLinkStart}users over license%{trueUpLinkEnd} on a quarterly or annual basis, depending on the terms of your agreement.`,
); );
export const subscriptionTable = {
activatedOn: s__('SuperSonics|Activated on'),
expiresOn: s__('SuperSonics|Expires on'),
seats: s__('SuperSonics|Seats'),
title: __('Subscription History'),
type: s__('SuperSonics|Type'),
validFrom: s__('SuperSonics|Valid From'),
};
export const subscriptionType = {
CLOUD: 'cloud',
LEGACY: 'legacy',
};
import { GlBadge } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import SubscriptionDetailsHistory from 'ee/pages/admin/cloud_licenses/components/subscription_details_history.vue';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import { license, subscriptionHistory } from '../mock_data';
describe('Subscription Details History', () => {
let wrapper;
const findTableRows = () => wrapper.findAllByTestId('subscription-history-row');
const cellFinder = (row) => (testId) => extendedWrapper(row).findByTestId(testId);
const containsABadge = (row) => row.findComponent(GlBadge).exists();
const createComponent = (props) => {
wrapper = extendedWrapper(
mount(SubscriptionDetailsHistory, {
propsData: {
currentSubscriptionId: license.ULTIMATE.id,
subscriptionList: subscriptionHistory,
...props,
},
}),
);
};
afterEach(() => {
wrapper.destroy();
});
describe('with data', () => {
beforeEach(() => {
createComponent();
});
it('has the correct number of rows', () => {
expect(findTableRows()).toHaveLength(2);
});
it('has the correct license type', () => {
expect(findTableRows().at(0).text()).toContain('Cloud License');
expect(findTableRows().at(1).text()).toContain('Legacy License');
});
it('has a badge for the license type', () => {
expect(findTableRows().wrappers.every(containsABadge)).toBe(true);
});
it('highlights the current subscription row', () => {
expect(findTableRows().at(0).classes('gl-text-blue-500')).toBe(true);
});
it('does not highlight the current subscription row', () => {
expect(findTableRows().at(1).classes('gl-text-blue-500')).toBe(false);
});
describe('cell data', () => {
let findCellByTestid;
beforeEach(() => {
createComponent();
findCellByTestid = cellFinder(findTableRows().at(0));
});
it.each`
testId | key
${'name'} | ${'name'}
${'email'} | ${'email'}
${'company'} | ${'company'}
${'plan'} | ${'plan'}
${'starts-at'} | ${'startsAt'}
${'valid-from'} | ${'validFrom'}
${'expires-at'} | ${'expiresAt'}
${'users-in-license'} | ${'usersInLicense'}
`('displays the correct value for the $testId cell', ({ testId, key }) => {
const cellTestId = `subscription-cell-${testId}`;
expect(findCellByTestid(cellTestId).text()).toBe(subscriptionHistory[0][key]);
});
it('displays the correct value for the type cell', () => {
const cellTestId = `subscription-cell-type`;
expect(findCellByTestid(cellTestId).text()).toBe('Cloud License');
});
});
});
describe('with no data', () => {
beforeEach(() => {
createComponent({
subscriptionList: [],
});
});
it('has the correct number of rows', () => {
expect(findTableRows()).toHaveLength(0);
});
});
});
import { subscriptionType } from 'ee/pages/admin/cloud_licenses/constants';
export const license = { export const license = {
ULTIMATE: { ULTIMATE: {
billableUsers: '8', billableUsers: '8',
...@@ -15,6 +17,35 @@ export const license = { ...@@ -15,6 +17,35 @@ export const license = {
}, },
}; };
export const subscriptionHistory = [
{
company: 'ACME Corp',
email: 'user@acmecorp.com',
expiresAt: '15-03-2022',
// TODO: verify presence in graphQL response
id: '1309188',
name: 'Jane Doe',
plan: 'Ultimate',
startsAt: '16-03-2021',
type: subscriptionType.CLOUD,
validFrom: '16-03-2021',
usersInLicense: '10',
},
{
company: 'ACME Corp',
email: 'user@acmecorp.com',
expiresAt: '30-06-2021',
// TODO: verify presence in graphQL response
id: '000000000',
name: 'Jane Doe',
plan: 'Ultimate',
startsAt: '01-07-2020',
type: subscriptionType.LEGACY,
validFrom: '01-07-2020',
usersInLicense: '5',
},
];
export const activateLicenseMutationResponse = { export const activateLicenseMutationResponse = {
FAILURE: [ FAILURE: [
{ {
......
...@@ -932,6 +932,9 @@ msgstr "" ...@@ -932,6 +932,9 @@ msgstr ""
msgid "%{total} warnings found: showing first %{warningsDisplayed}" msgid "%{total} warnings found: showing first %{warningsDisplayed}"
msgstr "" msgstr ""
msgid "%{type} License"
msgstr ""
msgid "%{usage_ping_link_start}Learn more%{usage_ping_link_end} about what information is shared with GitLab Inc." msgid "%{usage_ping_link_start}Learn more%{usage_ping_link_end} about what information is shared with GitLab Inc."
msgstr "" msgstr ""
...@@ -6483,42 +6486,15 @@ msgstr "" ...@@ -6483,42 +6486,15 @@ msgstr ""
msgid "CloudLicense|I agree that my use of the GitLab Software is subject to the Subscription Agreement located at the %{linkStart}Terms of Service%{linkEnd}, unless otherwise agreed to in writing with GitLab." msgid "CloudLicense|I agree that my use of the GitLab Software is subject to the Subscription Agreement located at the %{linkStart}Terms of Service%{linkEnd}, unless otherwise agreed to in writing with GitLab."
msgstr "" msgstr ""
msgid "CloudLicense|ID"
msgstr ""
msgid "CloudLicense|Last Sync"
msgstr ""
msgid "CloudLicense|Learn how to %{linkStart}activate your subscription%{linkEnd}." msgid "CloudLicense|Learn how to %{linkStart}activate your subscription%{linkEnd}."
msgstr "" msgstr ""
msgid "CloudLicense|Licensed to"
msgstr ""
msgid "CloudLicense|Manage"
msgstr ""
msgid "CloudLicense|Maximum users" msgid "CloudLicense|Maximum users"
msgstr "" msgstr ""
msgid "CloudLicense|Paste your activation code" msgid "CloudLicense|Paste your activation code"
msgstr "" msgstr ""
msgid "CloudLicense|Plan"
msgstr ""
msgid "CloudLicense|Renews"
msgstr ""
msgid "CloudLicense|Started"
msgstr ""
msgid "CloudLicense|Subscription details"
msgstr ""
msgid "CloudLicense|Sync Subscription details"
msgstr ""
msgid "CloudLicense|This instance is currently using the %{planName} plan." msgid "CloudLicense|This instance is currently using the %{planName} plan."
msgstr "" msgstr ""
...@@ -29715,6 +29691,9 @@ msgstr "" ...@@ -29715,6 +29691,9 @@ msgstr ""
msgid "Subscription" msgid "Subscription"
msgstr "" msgstr ""
msgid "Subscription History"
msgstr ""
msgid "Subscription deletion failed." msgid "Subscription deletion failed."
msgstr "" msgstr ""
...@@ -29961,6 +29940,48 @@ msgstr "" ...@@ -29961,6 +29940,48 @@ msgstr ""
msgid "Sunday" msgid "Sunday"
msgstr "" msgstr ""
msgid "SuperSonics|Activated on"
msgstr ""
msgid "SuperSonics|Expires on"
msgstr ""
msgid "SuperSonics|ID"
msgstr ""
msgid "SuperSonics|Last Sync"
msgstr ""
msgid "SuperSonics|Licensed to"
msgstr ""
msgid "SuperSonics|Manage"
msgstr ""
msgid "SuperSonics|Plan"
msgstr ""
msgid "SuperSonics|Renews"
msgstr ""
msgid "SuperSonics|Seats"
msgstr ""
msgid "SuperSonics|Started"
msgstr ""
msgid "SuperSonics|Subscription details"
msgstr ""
msgid "SuperSonics|Sync Subscription details"
msgstr ""
msgid "SuperSonics|Type"
msgstr ""
msgid "SuperSonics|Valid From"
msgstr ""
msgid "Support" msgid "Support"
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