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 @@ + + + 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 ""