Commit f9e98deb authored by Kushal Pandya's avatar Kushal Pandya

Merge branch 'usage-quotas-update-storage-tooltip-content' into 'master'

Usage quotas update storage tooltip content

See merge request gitlab-org/gitlab!45913
parents 541aff99 5f225a35
...@@ -169,7 +169,10 @@ export default { ...@@ -169,7 +169,10 @@ export default {
/> />
</div> </div>
</div> </div>
<projects-table :projects="namespaceProjects" /> <projects-table
:projects="namespaceProjects"
:additional-purchased-storage-size="namespace.additionalPurchasedStorageSize || 0"
/>
<temporary-storage-increase-modal <temporary-storage-increase-modal
v-if="isStorageIncreaseModalVisible" v-if="isStorageIncreaseModalVisible"
:limit="formattedNamespaceLimit" :limit="formattedNamespaceLimit"
......
...@@ -6,13 +6,27 @@ ...@@ -6,13 +6,27 @@
* lifted this component could replace and be used mainstream. * lifted this component could replace and be used mainstream.
*/ */
import { GlLink, GlIcon, GlTooltipDirective } from '@gitlab/ui'; import { GlLink, GlIcon, GlTooltipDirective } from '@gitlab/ui';
import { s__ } from '~/locale'; import { s__, sprintf } from '~/locale';
import ProjectAvatar from '~/vue_shared/components/project_avatar/default.vue'; import ProjectAvatar from '~/vue_shared/components/project_avatar/default.vue';
import { getIdFromGraphQLId } from '~/graphql_shared/utils'; import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { formatUsageSize, usageRatioToThresholdLevel } from '../utils'; import { formatUsageSize, usageRatioToThresholdLevel } from '../utils';
import { ALERT_THRESHOLD, ERROR_THRESHOLD, WARNING_THRESHOLD } from '../constants'; import { ALERT_THRESHOLD, ERROR_THRESHOLD, WARNING_THRESHOLD } from '../constants';
export default { export default {
i18n: {
warningWithNoPurchasedStorageText: s__(
'UsageQuota|This project is near the free %{actualRepositorySizeLimit} limit and at risk of being locked.',
),
lockedWithNoPurchasedStorageText: s__(
'UsageQuota|This project is locked because it is using %{actualRepositorySizeLimit} of free storage and there is no purchased storage available.',
),
warningWithPurchasedStorageText: s__(
'UsageQuota|This project is at risk of being locked because purchased storage is running low.',
),
lockedWithPurchasedStorageText: s__(
'UsageQuota|This project is locked because it used %{actualRepositorySizeLimit} of free storage and all the purchased storage.',
),
},
components: { components: {
GlIcon, GlIcon,
GlLink, GlLink,
...@@ -26,6 +40,10 @@ export default { ...@@ -26,6 +40,10 @@ export default {
required: true, required: true,
type: Object, type: Object,
}, },
additionalPurchasedStorageSize: {
type: Number,
required: true,
},
}, },
computed: { computed: {
projectAvatar() { projectAvatar() {
...@@ -40,6 +58,9 @@ export default { ...@@ -40,6 +58,9 @@ export default {
name() { name() {
return this.project.nameWithNamespace; return this.project.nameWithNamespace;
}, },
hasPurchasedStorage() {
return this.additionalPurchasedStorageSize > 0;
},
storageSize() { storageSize() {
return formatUsageSize(this.project.totalCalculatedUsedStorage); return formatUsageSize(this.project.totalCalculatedUsedStorage);
}, },
...@@ -53,21 +74,32 @@ export default { ...@@ -53,21 +74,32 @@ export default {
return usageRatioToThresholdLevel(this.excessStorageRatio); return usageRatioToThresholdLevel(this.excessStorageRatio);
}, },
status() { status() {
const i18nTextOpts = {
actualRepositorySizeLimit: formatUsageSize(this.project.actualRepositorySizeLimit),
};
if (this.thresholdLevel === ERROR_THRESHOLD) { if (this.thresholdLevel === ERROR_THRESHOLD) {
const tooltipText = this.hasPurchasedStorage
? this.$options.i18n.lockedWithPurchasedStorageText
: this.$options.i18n.lockedWithNoPurchasedStorageText;
return { return {
bgColor: { 'gl-bg-red-50': true }, bgColor: { 'gl-bg-red-50': true },
iconClass: { 'gl-text-red-500': true }, iconClass: { 'gl-text-red-500': true },
linkClass: 'gl-text-red-500!', linkClass: 'gl-text-red-500!',
tooltipText: s__('UsageQuota|This project is locked.'), tooltipText: sprintf(tooltipText, i18nTextOpts),
}; };
} else if ( } else if (
this.thresholdLevel === WARNING_THRESHOLD || this.thresholdLevel === WARNING_THRESHOLD ||
this.thresholdLevel === ALERT_THRESHOLD this.thresholdLevel === ALERT_THRESHOLD
) { ) {
const tooltipText = this.hasPurchasedStorage
? this.$options.i18n.warningWithPurchasedStorageText
: this.$options.i18n.warningWithNoPurchasedStorageText;
return { return {
bgColor: { 'gl-bg-orange-50': true }, bgColor: { 'gl-bg-orange-50': true },
iconClass: 'gl-text-orange-500', iconClass: 'gl-text-orange-500',
tooltipText: s__('UsageQuota|This project is at risk of being locked.'), tooltipText: sprintf(tooltipText, i18nTextOpts),
}; };
} }
......
...@@ -14,6 +14,10 @@ export default { ...@@ -14,6 +14,10 @@ export default {
type: Array, type: Array,
required: true, required: true,
}, },
additionalPurchasedStorageSize: {
type: Number,
required: true,
},
}, },
computed: { computed: {
isAdditionalStorageFlagEnabled() { isAdditionalStorageFlagEnabled() {
...@@ -61,6 +65,7 @@ export default { ...@@ -61,6 +65,7 @@ export default {
v-for="project in projects" v-for="project in projects"
:key="project.id" :key="project.id"
:project="project" :project="project"
:additional-purchased-storage-size="additionalPurchasedStorageSize"
/> />
</div> </div>
</template> </template>
...@@ -12,6 +12,7 @@ const createComponent = (propsData = {}) => { ...@@ -12,6 +12,7 @@ const createComponent = (propsData = {}) => {
wrapper = shallowMount(ProjectWithExcessStorage, { wrapper = shallowMount(ProjectWithExcessStorage, {
propsData: { propsData: {
project: projects[0], project: projects[0],
additionalPurchasedStorageSize: 0,
...propsData, ...propsData,
}, },
directives: { directives: {
...@@ -34,6 +35,7 @@ describe('Storage Counter project component', () => { ...@@ -34,6 +35,7 @@ describe('Storage Counter project component', () => {
wrapper.destroy(); wrapper.destroy();
}); });
describe('without extra storage purchased', () => {
it('renders project avatar', () => { it('renders project avatar', () => {
expect(wrapper.find(ProjectAvatar).exists()).toBe(true); expect(wrapper.find(ProjectAvatar).exists()).toBe(true);
}); });
...@@ -72,7 +74,9 @@ describe('Storage Counter project component', () => { ...@@ -72,7 +74,9 @@ describe('Storage Counter project component', () => {
}); });
it('with tooltip', () => { it('with tooltip', () => {
expect(getWarningIconTooltipText().title).toBe('This project is locked.'); expect(getWarningIconTooltipText().title).toBe(
'This project is locked because it is using 97.7KiB of free storage and there is no purchased storage available.',
);
}); });
}); });
...@@ -94,7 +98,50 @@ describe('Storage Counter project component', () => { ...@@ -94,7 +98,50 @@ describe('Storage Counter project component', () => {
}); });
it('with tooltip', () => { it('with tooltip', () => {
expect(getWarningIconTooltipText().title).toBe('This project is at risk of being locked.'); expect(getWarningIconTooltipText().title).toBe(
'This project is near the free 97.7KiB limit and at risk of being locked.',
);
});
});
});
describe('with extra storage purchased', () => {
describe('if projects is in error state', () => {
beforeEach(() => {
createComponent({
project: projects[2],
additionalPurchasedStorageSize: 100000,
});
});
afterEach(() => {
wrapper.destroy();
});
it('renders purchased storage specific error tooltip ', () => {
expect(getWarningIconTooltipText().title).toBe(
'This project is locked because it used 97.7KiB of free storage and all the purchased storage.',
);
});
});
describe('if projects is in warning state', () => {
beforeEach(() => {
createComponent({
project: projects[1],
additionalPurchasedStorageSize: 100000,
});
});
afterEach(() => {
wrapper.destroy();
});
it('renders purchased storage specific warning tooltip ', () => {
expect(getWarningIconTooltipText().title).toBe(
'This project is at risk of being locked because purchased storage is running low.',
);
});
}); });
}); });
}); });
...@@ -14,6 +14,7 @@ const createComponent = ({ additionalRepoStorageByNamespace = false } = {}) => { ...@@ -14,6 +14,7 @@ const createComponent = ({ additionalRepoStorageByNamespace = false } = {}) => {
wrapper = shallowMount(ProjectsTable, { wrapper = shallowMount(ProjectsTable, {
propsData: { propsData: {
projects, projects,
additionalPurchasedStorageSize: 0,
}, },
stubs, stubs,
provide: { provide: {
......
...@@ -28714,10 +28714,16 @@ msgstr "" ...@@ -28714,10 +28714,16 @@ msgstr ""
msgid "UsageQuota|This namespace has no projects which use shared runners" msgid "UsageQuota|This namespace has no projects which use shared runners"
msgstr "" msgstr ""
msgid "UsageQuota|This project is at risk of being locked." msgid "UsageQuota|This project is at risk of being locked because purchased storage is running low."
msgstr "" msgstr ""
msgid "UsageQuota|This project is locked." msgid "UsageQuota|This project is locked because it is using %{actualRepositorySizeLimit} of free storage and there is no purchased storage available."
msgstr ""
msgid "UsageQuota|This project is locked because it used %{actualRepositorySizeLimit} of free storage and all the purchased storage."
msgstr ""
msgid "UsageQuota|This project is near the free %{actualRepositorySizeLimit} limit and at risk of being locked."
msgstr "" msgstr ""
msgid "UsageQuota|Total excess storage used" msgid "UsageQuota|Total excess storage used"
......
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