From 4313f454e7e961b3041211691119cbe6201c6ec4 Mon Sep 17 00:00:00 2001 From: jajjibhai008 Date: Thu, 21 Nov 2024 15:49:13 +0500 Subject: [PATCH 1/3] feat: added dismissable warning banner on analytics v2 --- .../AdvanceAnalyticsV2/AnalyticsV2Page.jsx | 7 +++- .../AdvanceAnalyticsV2/WarningBanner.jsx | 29 +++++++++++++++++ .../AdvanceAnalyticsV2/data/constants.js | 2 ++ .../tests/WarningBanner.test.jsx | 32 +++++++++++++++++++ 4 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 src/components/AdvanceAnalyticsV2/WarningBanner.jsx create mode 100644 src/components/AdvanceAnalyticsV2/tests/WarningBanner.test.jsx diff --git a/src/components/AdvanceAnalyticsV2/AnalyticsV2Page.jsx b/src/components/AdvanceAnalyticsV2/AnalyticsV2Page.jsx index 1401e6e9af..df6bfe5e0e 100644 --- a/src/components/AdvanceAnalyticsV2/AnalyticsV2Page.jsx +++ b/src/components/AdvanceAnalyticsV2/AnalyticsV2Page.jsx @@ -6,6 +6,7 @@ import { Helmet } from 'react-helmet'; import PropTypes from 'prop-types'; import { FormattedMessage, useIntl } from '@edx/frontend-platform/i18n'; +import Cookies from 'universal-cookie'; import Hero from '../Hero'; import Stats from './Stats'; import Enrollments from './tabs/Enrollments'; @@ -14,7 +15,8 @@ import Completions from './tabs/Completions'; import Leaderboard from './tabs/Leaderboard'; import Skills from './tabs/Skills'; import { useEnterpriseAnalyticsAggregatesData } from './data/hooks'; -import { GRANULARITY, CALCULATION } from './data/constants'; +import { GRANULARITY, CALCULATION, ANALYTICS_WARNING_BANNER_COOKIE } from './data/constants'; +import WarningBanner from './WarningBanner'; const PAGE_TITLE = 'AnalyticsV2'; @@ -24,12 +26,14 @@ const AnalyticsV2Page = ({ enterpriseId }) => { const [calculation, setCalculation] = useState('Total'); const [startDate, setStartDate] = useState(); const [endDate, setEndDate] = useState(); + const cookies = new Cookies(); const intl = useIntl(); const { isFetching, isError, data } = useEnterpriseAnalyticsAggregatesData({ enterpriseCustomerUUID: enterpriseId, startDate, endDate, }); + const showWarningBanner = cookies.get(ANALYTICS_WARNING_BANNER_COOKIE); const currentDate = new Date().toISOString().split('T')[0]; const formatDate = (dateString) => { const options = { year: 'numeric', month: 'long', day: 'numeric' }; @@ -39,6 +43,7 @@ const AnalyticsV2Page = ({ enterpriseId }) => { <> + {!showWarningBanner && }
diff --git a/src/components/AdvanceAnalyticsV2/WarningBanner.jsx b/src/components/AdvanceAnalyticsV2/WarningBanner.jsx new file mode 100644 index 0000000000..84bf53320e --- /dev/null +++ b/src/components/AdvanceAnalyticsV2/WarningBanner.jsx @@ -0,0 +1,29 @@ +import React, { useState } from 'react'; +import Cookies from 'universal-cookie'; +import { + PageBanner, +} from '@openedx/paragon'; +import { ANALYTICS_WARNING_BANNER_COOKIE } from './data/constants'; + +const WarningBanner = () => { + const [showBanner, setShowBanner] = useState(true); + const cookies = new Cookies(); + + const onDismiss = () => { + setShowBanner(false); + cookies.set(ANALYTICS_WARNING_BANNER_COOKIE, true, { sameSite: 'strict' }); + }; + return ( + + 🚀 Analytics Just Got Better! We've updated charts, improved performance, + and now include audit enrollments for a more complete view of your data. + + ); +}; + +export default WarningBanner; diff --git a/src/components/AdvanceAnalyticsV2/data/constants.js b/src/components/AdvanceAnalyticsV2/data/constants.js index bb0a88a39a..74cf32ded1 100644 --- a/src/components/AdvanceAnalyticsV2/data/constants.js +++ b/src/components/AdvanceAnalyticsV2/data/constants.js @@ -114,3 +114,5 @@ export const CALCULATION = { MOVING_AVERAGE_3_PERIODS: 'moving-average-3-period', MOVING_AVERAGE_7_PERIODS: 'moving-average-7-period', }; + +export const ANALYTICS_WARNING_BANNER_COOKIE = 'hide-warning-banner-on-analytics'; diff --git a/src/components/AdvanceAnalyticsV2/tests/WarningBanner.test.jsx b/src/components/AdvanceAnalyticsV2/tests/WarningBanner.test.jsx new file mode 100644 index 0000000000..38ce814657 --- /dev/null +++ b/src/components/AdvanceAnalyticsV2/tests/WarningBanner.test.jsx @@ -0,0 +1,32 @@ +import React from 'react'; +import { render, screen, fireEvent } from '@testing-library/react'; +import Cookies from 'universal-cookie'; +import WarningBanner from '../WarningBanner'; +import '@testing-library/jest-dom'; + +// jest.mock('universal-cookie'); +jest.mock('universal-cookie', () => jest.fn().mockImplementation(() => ({ + set: jest.fn(), +}))); + +describe('WarningBanner', () => { + let cookies; + + beforeEach(() => { + cookies = new Cookies(); + cookies.set = jest.fn(); + }); + + test('should trigger onDismiss and set cookie in warning banner', () => { + render(); + + // Check that the banner is initially shown + expect(screen.getByText(/Analytics Just Got Better!/i)).toBeInTheDocument(); + + // Trigger the onDismiss function + fireEvent.click(screen.getByRole('button', { name: /Dismiss/i })); + + // Check that the banner is no longer shown + expect(screen.queryByText(/Analytics Just Got Better!/i)).not.toBeInTheDocument(); + }); +}); From bc2aad6cb6c65e47ec0921783a4e2a6a64354f16 Mon Sep 17 00:00:00 2001 From: muhammad-ammar Date: Wed, 9 Oct 2024 15:27:59 +0500 Subject: [PATCH 2/3] feat: ship analytics 2.0 --- src/components/AdvanceAnalyticsV2/AnalyticsV2Page.jsx | 2 +- src/components/EnterpriseApp/EnterpriseAppRoutes.jsx | 8 ++++---- .../EnterpriseApp/EnterpriseAppRoutes.test.jsx | 9 +-------- src/components/EnterpriseApp/data/constants.js | 2 +- src/components/Sidebar/index.jsx | 6 ------ src/config/index.js | 1 - 6 files changed, 7 insertions(+), 21 deletions(-) diff --git a/src/components/AdvanceAnalyticsV2/AnalyticsV2Page.jsx b/src/components/AdvanceAnalyticsV2/AnalyticsV2Page.jsx index df6bfe5e0e..cde5a8014e 100644 --- a/src/components/AdvanceAnalyticsV2/AnalyticsV2Page.jsx +++ b/src/components/AdvanceAnalyticsV2/AnalyticsV2Page.jsx @@ -18,7 +18,7 @@ import { useEnterpriseAnalyticsAggregatesData } from './data/hooks'; import { GRANULARITY, CALCULATION, ANALYTICS_WARNING_BANNER_COOKIE } from './data/constants'; import WarningBanner from './WarningBanner'; -const PAGE_TITLE = 'AnalyticsV2'; +const PAGE_TITLE = 'Analytics'; const AnalyticsV2Page = ({ enterpriseId }) => { const [activeTab, setActiveTab] = useState('enrollments'); diff --git a/src/components/EnterpriseApp/EnterpriseAppRoutes.jsx b/src/components/EnterpriseApp/EnterpriseAppRoutes.jsx index 972f7b77f4..a44e9f05fa 100644 --- a/src/components/EnterpriseApp/EnterpriseAppRoutes.jsx +++ b/src/components/EnterpriseApp/EnterpriseAppRoutes.jsx @@ -82,17 +82,17 @@ const EnterpriseAppRoutes = ({ /> )} - {enableAnalyticsPage && enterpriseAppPage === ROUTE_NAMES.analytics && ( + {enableAnalyticsPage && enterpriseAppPage === ROUTE_NAMES.legacyAnalytics && ( : } /> )} - {enableAnalyticsPage && enterpriseAppPage === ROUTE_NAMES.analyticsv2 && ( + {enableAnalyticsPage && enterpriseAppPage === ROUTE_NAMES.analytics && ( diff --git a/src/components/EnterpriseApp/EnterpriseAppRoutes.test.jsx b/src/components/EnterpriseApp/EnterpriseAppRoutes.test.jsx index d706470f45..22a7d6cc22 100644 --- a/src/components/EnterpriseApp/EnterpriseAppRoutes.test.jsx +++ b/src/components/EnterpriseApp/EnterpriseAppRoutes.test.jsx @@ -16,7 +16,7 @@ jest.mock('../PlotlyAnalytics', () => ({ PlotlyAnalyticsPage: () =>
PlotlyAnalyticsPage Mock Component
, })); -let mockEnterpriseAppPage = 'analyticsv2'; +let mockEnterpriseAppPage = 'analytics'; jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), @@ -67,11 +67,4 @@ describe('EnterpriseAppRoutes', () => { renderWithProviders(defaultProps); expect(screen.getByText('AdminPage Mock Component')).toBeInTheDocument(); }); - - it('renders Analytics when ANALYTICS_SUPPORTED is true', () => { - mockEnterpriseAppPage = 'analytics'; - features.ANALYTICS_SUPPORTED = true; - renderWithProviders(defaultProps); - expect(screen.getByText('PlotlyAnalyticsPage Mock Component')).toBeInTheDocument(); - }); }); diff --git a/src/components/EnterpriseApp/data/constants.js b/src/components/EnterpriseApp/data/constants.js index 93bd9412d1..71ca6764fe 100644 --- a/src/components/EnterpriseApp/data/constants.js +++ b/src/components/EnterpriseApp/data/constants.js @@ -1,8 +1,8 @@ /* eslint-disable import/prefer-default-export */ export const ROUTE_NAMES = { + legacyAnalytics: 'legacy-analytics', analytics: 'analytics', - analyticsv2: 'analyticsv2', appearance: 'appearance', bulkEnrollment: 'enrollment', bulkEnrollmentResults: 'bulk-enrollment-results', diff --git a/src/components/Sidebar/index.jsx b/src/components/Sidebar/index.jsx index bb5f0891f3..3913813a5f 100644 --- a/src/components/Sidebar/index.jsx +++ b/src/components/Sidebar/index.jsx @@ -125,12 +125,6 @@ const Sidebar = ({ icon: , hidden: !features.ANALYTICS || !enableAnalyticsScreen, }, - { - title: 'AnalyticsV2', - to: `${baseUrl}/admin/${ROUTE_NAMES.analyticsv2}`, - icon: , - hidden: !features.ANALYTICS_V2, - }, { title: 'Code Management', to: `${baseUrl}/admin/${ROUTE_NAMES.codeManagement}`, diff --git a/src/config/index.js b/src/config/index.js index cc3b930b95..b2f4f57f23 100644 --- a/src/config/index.js +++ b/src/config/index.js @@ -44,7 +44,6 @@ const features = { CODE_MANAGEMENT: process.env.FEATURE_CODE_MANAGEMENT || hasFeatureFlagEnabled('CODE_MANAGEMENT'), REPORTING_CONFIGURATIONS: process.env.FEATURE_REPORTING_CONFIGURATIONS || hasFeatureFlagEnabled('REPORTING_CONFIGURATIONS'), ANALYTICS: process.env.FEATURE_ANALYTICS || hasFeatureFlagEnabled('ANALYTICS'), - ANALYTICS_V2: process.env.FEATURE_ANALYTICS_V2 || hasFeatureFlagEnabled('ANALYTICS_V2'), ANALYTICS_SUPPORTED: process.env.ANALYTICS_SUPPORTED || hasFeatureFlagEnabled('ANALYTICS_SUPPORTED'), SAML_CONFIGURATION: process.env.FEATURE_SAML_CONFIGURATION || hasFeatureFlagEnabled('SAML_CONFIGURATION'), SUPPORT: process.env.FEATURE_SUPPORT || hasFeatureFlagEnabled('SUPPORT'), From 3bb76507524c3f2ae1f4400d8f2daa70503ffcfe Mon Sep 17 00:00:00 2001 From: Marlon Keating <322346+marlonkeating@users.noreply.github.com> Date: Fri, 22 Nov 2024 11:29:33 -0800 Subject: [PATCH 3/3] fix: refresh group list after adding group (#1349) fix: Invalidate groups query after new members are invited --- src/components/PeopleManagement/CreateGroupModal.jsx | 6 ++++++ .../PeopleManagement/tests/PeopleManagementPage.test.jsx | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/src/components/PeopleManagement/CreateGroupModal.jsx b/src/components/PeopleManagement/CreateGroupModal.jsx index 6630c65cfd..16b7802866 100644 --- a/src/components/PeopleManagement/CreateGroupModal.jsx +++ b/src/components/PeopleManagement/CreateGroupModal.jsx @@ -2,6 +2,7 @@ import React, { useCallback, useState, useEffect } from 'react'; import { logError } from '@edx/frontend-platform/logging'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; +import { useQueryClient } from '@tanstack/react-query'; import { useIntl } from '@edx/frontend-platform/i18n'; import { snakeCaseObject } from '@edx/frontend-platform/utils'; import { @@ -10,6 +11,7 @@ import { import LmsApiService from '../../data/services/LmsApiService'; import SystemErrorAlertModal from '../learner-credit-management/cards/assignment-allocation-status-modals/SystemErrorAlertModal'; import CreateGroupModalContent from './CreateGroupModalContent'; +import { learnerCreditManagementQueryKeys } from '../learner-credit-management/data'; const CreateGroupModal = ({ isModalOpen, @@ -27,6 +29,7 @@ const CreateGroupModal = ({ closeModal(); setCreateButtonState('default'); }; + const queryClient = useQueryClient(); const handleCreateGroup = async () => { setCreateButtonState('pending'); @@ -49,6 +52,9 @@ const CreateGroupModal = ({ learnerEmails, }); await LmsApiService.inviteEnterpriseLearnersToGroup(groupCreationResponse.data.uuid, requestBody); + queryClient.invalidateQueries({ + queryKey: learnerCreditManagementQueryKeys.group(enterpriseUUID), + }); setCreateButtonState('complete'); handleCloseCreateGroupModal(); } catch (err) { diff --git a/src/components/PeopleManagement/tests/PeopleManagementPage.test.jsx b/src/components/PeopleManagement/tests/PeopleManagementPage.test.jsx index 03f15c5f2a..fb69b2f231 100644 --- a/src/components/PeopleManagement/tests/PeopleManagementPage.test.jsx +++ b/src/components/PeopleManagement/tests/PeopleManagementPage.test.jsx @@ -33,6 +33,11 @@ const subsEnterpriseSubsidiesContextValue = { isLoading: false, }; +jest.mock('@tanstack/react-query', () => ({ + ...jest.requireActual('@tanstack/react-query'), + useQueryClient: jest.fn(), +})); + jest.mock('../../learner-credit-management/data', () => ({ ...jest.requireActual('../../learner-credit-management/data'), useAllEnterpriseGroups: jest.fn(),