Commit 6bad5af3 authored by Angelo Gulina's avatar Angelo Gulina Committed by Savas Vedova

Subscription Details: License History Table

parent f0e7574b
<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';
export const subscriptionDetailsHeaderText = s__('CloudLicense|Subscription details');
export const licensedToHeaderText = s__('CloudLicense|Licensed to');
export const manageSubscriptionButtonText = s__('CloudLicense|Manage');
export const syncSubscriptionButtonText = s__('CloudLicense|Sync Subscription details');
export const subscriptionDetailsHeaderText = s__('SuperSonics|Subscription details');
export const licensedToHeaderText = s__('SuperSonics|Licensed to');
export const manageSubscriptionButtonText = s__('SuperSonics|Manage');
export const syncSubscriptionButtonText = s__('SuperSonics|Sync Subscription details');
export const copySubscriptionIdButtonText = __('Copy');
export const subscriptionTypeText = __('%{type} License');
export const detailsLabels = {
address: __('Address'),
id: s__('CloudLicense|ID'),
id: s__('SuperSonics|ID'),
company: __('Company'),
email: __('Email'),
lastSync: s__('CloudLicense|Last Sync'),
lastSync: s__('SuperSonics|Last Sync'),
name: __('Name'),
plan: s__('CloudLicense|Plan'),
startsAt: s__('CloudLicense|Started'),
renews: s__('CloudLicense|Renews'),
plan: s__('SuperSonics|Plan'),
renews: s__('SuperSonics|Renews'),
startsAt: s__('SuperSonics|Started'),
};
export const billableUsersTitle = s__('CloudLicense|Billable users');
......@@ -33,3 +34,15 @@ export const usersInSubscriptionText = 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.`,
);
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 = {
ULTIMATE: {
billableUsers: '8',
......@@ -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 = {
FAILURE: [
{
......
......@@ -932,6 +932,9 @@ msgstr ""
msgid "%{total} warnings found: showing first %{warningsDisplayed}"
msgstr ""
msgid "%{type} License"
msgstr ""
msgid "%{usage_ping_link_start}Learn more%{usage_ping_link_end} about what information is shared with GitLab Inc."
msgstr ""
......@@ -6450,42 +6453,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."
msgstr ""
msgid "CloudLicense|ID"
msgstr ""
msgid "CloudLicense|Last Sync"
msgstr ""
msgid "CloudLicense|Learn how to %{linkStart}activate your subscription%{linkEnd}."
msgstr ""
msgid "CloudLicense|Licensed to"
msgstr ""
msgid "CloudLicense|Manage"
msgstr ""
msgid "CloudLicense|Maximum users"
msgstr ""
msgid "CloudLicense|Paste your activation code"
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."
msgstr ""
......@@ -29646,6 +29622,9 @@ msgstr ""
msgid "Subscription"
msgstr ""
msgid "Subscription History"
msgstr ""
msgid "Subscription deletion failed."
msgstr ""
......@@ -29892,6 +29871,48 @@ msgstr ""
msgid "Sunday"
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"
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