summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/vue_shared/components/storage_counter
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/vue_shared/components/storage_counter')
-rw-r--r--app/assets/javascripts/vue_shared/components/storage_counter/usage_graph.stories.js38
-rw-r--r--app/assets/javascripts/vue_shared/components/storage_counter/usage_graph.vue148
2 files changed, 186 insertions, 0 deletions
diff --git a/app/assets/javascripts/vue_shared/components/storage_counter/usage_graph.stories.js b/app/assets/javascripts/vue_shared/components/storage_counter/usage_graph.stories.js
new file mode 100644
index 00000000000..00aa5519ec6
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/storage_counter/usage_graph.stories.js
@@ -0,0 +1,38 @@
+/* eslint-disable @gitlab/require-i18n-strings */
+import '@gitlab/ui/dist/utility_classes.css';
+import UsageGraph from './usage_graph.vue';
+
+export default {
+ component: UsageGraph,
+ title: 'vue_shared/components/storage_counter/usage_graph',
+};
+
+const Template = (args, { argTypes }) => ({
+ components: { UsageGraph },
+ props: Object.keys(argTypes),
+ template: '<usage-graph v-bind="$props" />',
+});
+
+export const Default = Template.bind({});
+Default.argTypes = {
+ rootStorageStatistics: {
+ description: 'The statistics object with all its fields',
+ type: { name: 'object', required: true },
+ defaultValue: {
+ buildArtifactsSize: 400000,
+ pipelineArtifactsSize: 38000,
+ lfsObjectsSize: 4800000,
+ packagesSize: 3800000,
+ repositorySize: 39000000,
+ snippetsSize: 2000112,
+ storageSize: 39930000,
+ uploadsSize: 7000,
+ wikiSize: 300000,
+ },
+ },
+ limit: {
+ description:
+ 'When a limit is set, users will see how much of their storage usage (limit) is used. In case the limit is 0 or the current usage exceeds the limit, it just renders the distribution',
+ defaultValue: 0,
+ },
+};
diff --git a/app/assets/javascripts/vue_shared/components/storage_counter/usage_graph.vue b/app/assets/javascripts/vue_shared/components/storage_counter/usage_graph.vue
new file mode 100644
index 00000000000..c33d065ff4b
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/storage_counter/usage_graph.vue
@@ -0,0 +1,148 @@
+<script>
+import { GlIcon, GlTooltipDirective } from '@gitlab/ui';
+import { numberToHumanSize } from '~/lib/utils/number_utils';
+import { s__ } from '~/locale';
+
+export default {
+ components: {
+ GlIcon,
+ },
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ props: {
+ rootStorageStatistics: {
+ required: true,
+ type: Object,
+ },
+ limit: {
+ required: true,
+ type: Number,
+ },
+ },
+ computed: {
+ storageTypes() {
+ const {
+ buildArtifactsSize,
+ pipelineArtifactsSize,
+ lfsObjectsSize,
+ packagesSize,
+ repositorySize,
+ storageSize,
+ wikiSize,
+ snippetsSize,
+ uploadsSize,
+ } = this.rootStorageStatistics;
+ const artifactsSize = buildArtifactsSize + pipelineArtifactsSize;
+
+ if (storageSize === 0) {
+ return null;
+ }
+
+ return [
+ {
+ name: s__('UsageQuota|Repositories'),
+ style: this.usageStyle(this.barRatio(repositorySize)),
+ class: 'gl-bg-data-viz-blue-500',
+ size: repositorySize,
+ },
+ {
+ name: s__('UsageQuota|LFS Objects'),
+ style: this.usageStyle(this.barRatio(lfsObjectsSize)),
+ class: 'gl-bg-data-viz-orange-600',
+ size: lfsObjectsSize,
+ },
+ {
+ name: s__('UsageQuota|Packages'),
+ style: this.usageStyle(this.barRatio(packagesSize)),
+ class: 'gl-bg-data-viz-aqua-500',
+ size: packagesSize,
+ },
+ {
+ name: s__('UsageQuota|Artifacts'),
+ style: this.usageStyle(this.barRatio(artifactsSize)),
+ class: 'gl-bg-data-viz-green-600',
+ size: artifactsSize,
+ tooltip: s__('UsageQuota|Artifacts is a sum of build and pipeline artifacts.'),
+ },
+ {
+ name: s__('UsageQuota|Wikis'),
+ style: this.usageStyle(this.barRatio(wikiSize)),
+ class: 'gl-bg-data-viz-magenta-500',
+ size: wikiSize,
+ },
+ {
+ name: s__('UsageQuota|Snippets'),
+ style: this.usageStyle(this.barRatio(snippetsSize)),
+ class: 'gl-bg-data-viz-orange-800',
+ size: snippetsSize,
+ },
+ {
+ name: s__('UsageQuota|Uploads'),
+ style: this.usageStyle(this.barRatio(uploadsSize)),
+ class: 'gl-bg-data-viz-aqua-700',
+ size: uploadsSize,
+ },
+ ]
+ .filter((data) => data.size !== 0)
+ .sort((a, b) => b.size - a.size);
+ },
+ },
+ methods: {
+ formatSize(size) {
+ return numberToHumanSize(size);
+ },
+ usageStyle(ratio) {
+ return { flex: ratio };
+ },
+ barRatio(size) {
+ let max = this.rootStorageStatistics.storageSize;
+
+ if (this.limit !== 0 && max <= this.limit) {
+ max = this.limit;
+ }
+
+ return size / max;
+ },
+ },
+};
+</script>
+<template>
+ <div v-if="storageTypes" class="gl-display-flex gl-flex-direction-column w-100">
+ <div class="gl-h-6 gl-my-5 gl-bg-gray-50 gl-rounded-base gl-display-flex">
+ <div
+ v-for="storageType in storageTypes"
+ :key="storageType.name"
+ class="storage-type-usage gl-h-full gl-display-inline-block"
+ :class="storageType.class"
+ :style="storageType.style"
+ data-testid="storage-type-usage"
+ ></div>
+ </div>
+ <div class="row py-0">
+ <div
+ v-for="storageType in storageTypes"
+ :key="storageType.name"
+ class="col-md-auto gl-display-flex gl-align-items-center"
+ data-testid="storage-type-legend"
+ >
+ <div class="gl-h-2 gl-w-5 gl-mr-2 gl-display-inline-block" :class="storageType.class"></div>
+ <span class="gl-mr-2 gl-font-weight-bold gl-font-sm">
+ {{ storageType.name }}
+ </span>
+ <span class="gl-text-gray-500 gl-font-sm">
+ {{ formatSize(storageType.size) }}
+ </span>
+ <span
+ v-if="storageType.tooltip"
+ v-gl-tooltip
+ :title="storageType.tooltip"
+ :aria-label="storageType.tooltip"
+ class="gl-ml-2"
+ >
+ <gl-icon name="question" :size="12" />
+ </span>
+ </div>
+ </div>
+ </div>
+</template>