diff --git a/ee/app/assets/javascripts/pages/admin/gitlab_duo/usage/index.js b/ee/app/assets/javascripts/pages/admin/gitlab_duo/usage/index.js
index c1d00af3b4132b99928322369578dcbedd7c0c3a..174bc61b72f39160add59011d3e5457b367c7926 100644
--- a/ee/app/assets/javascripts/pages/admin/gitlab_duo/usage/index.js
+++ b/ee/app/assets/javascripts/pages/admin/gitlab_duo/usage/index.js
@@ -1,21 +1,18 @@
import Vue from 'vue';
-import AdminUsageDashboard from 'ee/usage_quotas/usage_billing/components/app.vue';
+import UsageBillingDashboard from 'ee/usage_quotas/usage_billing/components/app.vue';
-function initAdminUsageDashboard() {
+function initUsageBillingDashboard() {
const el = document.getElementById('js-instance-usage-billing-dashboard');
- if (!el) {
- return null;
- }
+ if (!el) return null;
return new Vue({
el,
- name: 'AdminUsageBillingDashboardApp',
- provide: {},
+ name: 'AdminUsageBillingDashboardView',
render(createElement) {
- return createElement(AdminUsageDashboard);
+ return createElement(UsageBillingDashboard);
},
});
}
-initAdminUsageDashboard();
+initUsageBillingDashboard();
diff --git a/ee/app/assets/javascripts/usage_quotas/usage_billing/components/formatted_date_range.vue b/ee/app/assets/javascripts/usage_quotas/usage_billing/components/formatted_date_range.vue
new file mode 100644
index 0000000000000000000000000000000000000000..edddedd8737f6f7cba9764e2abc6d22c182a173d
--- /dev/null
+++ b/ee/app/assets/javascripts/usage_quotas/usage_billing/components/formatted_date_range.vue
@@ -0,0 +1,26 @@
+
+
+
+ {{ formattedRange }}
+
diff --git a/ee/app/assets/javascripts/usage_quotas/usage_billing/utils.js b/ee/app/assets/javascripts/usage_quotas/usage_billing/utils.js
new file mode 100644
index 0000000000000000000000000000000000000000..f8e1c95384318c3dd2f2d0f93421ea4a4bb42322
--- /dev/null
+++ b/ee/app/assets/javascripts/usage_quotas/usage_billing/utils.js
@@ -0,0 +1,33 @@
+import { s__ } from '~/locale';
+
+/**
+ * Formats two ISO dates into a date range
+ *
+ * @param {String} start
+ * @param {String} end
+ * @returns Formatted range, i.e. 1 Jul - 31 Jul, 2025
+ */
+export function dateRangeFormatted(start, end) {
+ const startDate = new Date(start);
+ const endDate = new Date(end);
+
+ if (Number.isNaN(startDate.getTime()) || Number.isNaN(endDate.getTime())) {
+ throw new Error(s__('UsageBilling|Invalid date provided'));
+ }
+
+ const sameYear = startDate.getFullYear() === endDate.getFullYear();
+
+ const formatDate = (date, includeYear = true) => {
+ const day = date.getDate();
+ const month = date.toLocaleDateString('en-US', { month: 'short' });
+ const year = date.getFullYear();
+
+ return includeYear ? `${day} ${month}, ${year}` : `${day} ${month}`;
+ };
+
+ if (sameYear) {
+ return `${formatDate(startDate, false)} - ${formatDate(endDate, true)}`;
+ }
+
+ return `${formatDate(startDate, true)} - ${formatDate(endDate, true)}`;
+}
diff --git a/ee/spec/frontend/usage_quotas/usage_billing/components/formatted_date_range_spec.js b/ee/spec/frontend/usage_quotas/usage_billing/components/formatted_date_range_spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..0093e6fb7f39600210680338e4cdac65ffc4a8a1
--- /dev/null
+++ b/ee/spec/frontend/usage_quotas/usage_billing/components/formatted_date_range_spec.js
@@ -0,0 +1,47 @@
+import { shallowMount } from '@vue/test-utils';
+import FormattedDateRange from 'ee/usage_quotas/usage_billing/components/formatted_date_range.vue';
+
+describe('FormattedDateRange', () => {
+ let wrapper;
+
+ const defaultProps = {
+ start: '2024-01-01',
+ end: '2024-01-31',
+ };
+
+ const createComponent = (props = {}) => {
+ wrapper = shallowMount(FormattedDateRange, {
+ propsData: { ...defaultProps, ...props },
+ });
+ };
+
+ beforeAll(() => {
+ jest.useFakeTimers({ legacyFakeTimers: false });
+
+ jest.setSystemTime(new Date('2024-01-15'));
+ });
+
+ it('renders the formatted date range', () => {
+ createComponent();
+
+ expect(wrapper.text()).toBe('1 Jan - 31 Jan, 2024');
+ });
+
+ it('renders different date range when props change', () => {
+ createComponent({
+ start: '2024-02-01',
+ end: '2024-02-29',
+ });
+
+ expect(wrapper.text()).toBe('1 Feb - 29 Feb, 2024');
+ });
+
+ it('handles cross-year date ranges', () => {
+ createComponent({
+ start: '2023-12-01',
+ end: '2024-01-31',
+ });
+
+ expect(wrapper.text()).toBe('1 Dec, 2023 - 31 Jan, 2024');
+ });
+});
diff --git a/ee/spec/frontend/usage_quotas/usage_billing/utils_spec.js b/ee/spec/frontend/usage_quotas/usage_billing/utils_spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..bd04057d71b0250498558899d96cad76caf87380
--- /dev/null
+++ b/ee/spec/frontend/usage_quotas/usage_billing/utils_spec.js
@@ -0,0 +1,38 @@
+import { dateRangeFormatted } from 'ee/usage_quotas/usage_billing/utils';
+
+describe('dateRangeFormatted', () => {
+ it('formats date range within the same year', () => {
+ const start = '2025-07-01';
+ const end = '2025-07-31';
+
+ expect(dateRangeFormatted(start, end)).toBe('1 Jul - 31 Jul, 2025');
+ });
+
+ it('formats date range across different years', () => {
+ const start = '2024-12-15';
+ const end = '2025-01-15';
+
+ expect(dateRangeFormatted(start, end)).toBe('15 Dec, 2024 - 15 Jan, 2025');
+ });
+
+ it('formats date range with single digit dates', () => {
+ const start = '2025-01-01';
+ const end = '2025-12-31';
+
+ expect(dateRangeFormatted(start, end)).toBe('1 Jan - 31 Dec, 2025');
+ });
+
+ it('formats date range with same start and end date', () => {
+ const start = '2025-06-15';
+ const end = '2025-06-15';
+
+ expect(dateRangeFormatted(start, end)).toBe('15 Jun - 15 Jun, 2025');
+ });
+
+ it('throws an error if the rovided dates are invalid', () => {
+ const start = 'invalid-date';
+ const end = '2025-06-15';
+
+ expect(() => dateRangeFormatted(start, end)).toThrow('Invalid date provided');
+ });
+});
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 832b217bcd352a398fa56e680ab5183526a6500e..f401473e76ccd5637d6f59099b658698c10fc5fb 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -68786,6 +68786,9 @@ msgstr ""
msgid "UsageBilling|Current month usage"
msgstr ""
+msgid "UsageBilling|Invalid date provided"
+msgstr ""
+
msgid "UsageBilling|Purchase a monthly commitment"
msgstr ""