diff --git a/src/components/learner-credit-management/BudgetDetailActivityTabContents.jsx b/src/components/learner-credit-management/BudgetDetailActivityTabContents.jsx
index d8ada45302..9a5774a0e0 100644
--- a/src/components/learner-credit-management/BudgetDetailActivityTabContents.jsx
+++ b/src/components/learner-credit-management/BudgetDetailActivityTabContents.jsx
@@ -52,7 +52,6 @@ const BudgetDetailActivityTabContents = ({ enterpriseUUID, enterpriseFeatures })
&& (
)}
{hasSpentTransactions && }
diff --git a/src/components/learner-credit-management/BudgetDetailRedemptions.jsx b/src/components/learner-credit-management/BudgetDetailRedemptions.jsx
index a69e8e45e0..4a7c8a50ec 100644
--- a/src/components/learner-credit-management/BudgetDetailRedemptions.jsx
+++ b/src/components/learner-credit-management/BudgetDetailRedemptions.jsx
@@ -47,7 +47,7 @@ const BudgetDetailRedemptions = ({ enterpriseFeatures, enterpriseUUID }) => {
{(enterpriseOfferId || (subsidyAccessPolicyId && !enterpriseFeatures.topDownAssignmentRealTimeLcm)) ? (
diff --git a/src/components/learner-credit-management/BudgetStatusSubtitle.jsx b/src/components/learner-credit-management/BudgetStatusSubtitle.jsx
index 75b0b7b238..f68c54fb84 100644
--- a/src/components/learner-credit-management/BudgetStatusSubtitle.jsx
+++ b/src/components/learner-credit-management/BudgetStatusSubtitle.jsx
@@ -15,6 +15,7 @@ const BudgetStatusSubtitle = ({
}) => {
const { data: enterpriseGroup } = useEnterpriseGroup(policy);
const { data: enterpriseCustomer } = useEnterpriseCustomer(enterpriseUUID);
+ // universal group = all members of the organization are automatically in a group
const universalGroup = enterpriseGroup?.appliesToAllContexts;
const intl = useIntl();
const budgetType = {
@@ -40,24 +41,32 @@ const BudgetStatusSubtitle = ({
defaultMessage: 'Assignment',
description: 'Enrollment type for budgets that are assignable',
}),
+ },
+ browseAndEnroll: {
+ enrollmentType:
+ intl.formatMessage({
+ id: 'lcm.budget.detail.page.overview.enroll.browse.and.enroll',
+ defaultMessage: 'Browse & Enroll',
+ description: 'Enrollment type for budgets that are browsable and enrollable',
+ }),
popoverText:
intl.formatMessage({
- id: 'lcm.budget.detail.page.overview.enroll.assignable.popover',
+ id: 'lcm.budget.detail.page.overview.enroll.browse.and.enroll.popover',
defaultMessage: 'Available to members added to this budget',
- description: 'Popover text for budgets that are assignable',
+ description: 'Popover text for budgets that are browsable and enrollable',
}),
icon: ,
},
- browseAndEnroll: {
+ orgBrowseAndEnroll: {
enrollmentType:
intl.formatMessage({
- id: 'lcm.budget.detail.page.overview.enroll.browse.and.enroll',
+ id: 'lcm.budget.detail.page.overview.enroll.org.browse.and.enroll',
defaultMessage: 'Browse & Enroll',
description: 'Enrollment type for budgets that are browsable and enrollable',
}),
popoverText:
intl.formatMessage({
- id: 'lcm.budget.detail.page.overview.enroll.browse.and.enroll.popover',
+ id: 'lcm.budget.detail.page.overview.enroll.org.browse.and.enroll.popover',
defaultMessage: 'Available to all people in your organization',
description: 'Popover text for budgets that are browsable and enrollable',
}),
@@ -68,6 +77,8 @@ const BudgetStatusSubtitle = ({
if (isLmsBudget(enterpriseCustomer?.activeIntegrations.length, universalGroup)) {
budgetTypeToRender = budgetType.lms;
+ } else if (universalGroup) {
+ budgetTypeToRender = budgetType.orgBrowseAndEnroll;
} else if (isAssignable) {
budgetTypeToRender = budgetType.assignable;
} else {
diff --git a/src/components/learner-credit-management/empty-state/NoBnEBudgetActivity.jsx b/src/components/learner-credit-management/empty-state/NoBnEBudgetActivity.jsx
index 94795cdbd9..5991139706 100644
--- a/src/components/learner-credit-management/empty-state/NoBnEBudgetActivity.jsx
+++ b/src/components/learner-credit-management/empty-state/NoBnEBudgetActivity.jsx
@@ -5,8 +5,12 @@ import {
Button, Card, Col, Row,
} from '@openedx/paragon';
import { Link } from 'react-router-dom';
-
-import { useIsLargeOrGreater } from '../data';
+import {
+ useBudgetId,
+ useEnterpriseGroupLearners,
+ useIsLargeOrGreater,
+ useSubsidyAccessPolicy,
+} from '../data';
import nameYourMembers from '../assets/reading.svg';
import memberBrowse from '../assets/phoneScroll.svg';
import enrollAndSpend from '../assets/wallet.svg';
@@ -23,7 +27,12 @@ const EnrollAndSpendIllustration = (props) => (
);
-const NoBnEBudgetActivity = ({ openInviteModal, isEnterpriseGroupsEnabled }) => {
+const NoBnEBudgetActivity = ({ openInviteModal }) => {
+ const { subsidyAccessPolicyId } = useBudgetId();
+ const { data: subsidyAccessPolicy } = useSubsidyAccessPolicy(subsidyAccessPolicyId);
+ const { data } = useEnterpriseGroupLearners(subsidyAccessPolicy?.groupAssociations[0]);
+ const groupMembersCount = data?.count || 0;
+
const isLargeOrGreater = useIsLargeOrGreater();
return (
@@ -89,7 +98,7 @@ const NoBnEBudgetActivity = ({ openInviteModal, isEnterpriseGroupsEnabled }) =>
as={Link}
onClick={openInviteModal}
>
- {isEnterpriseGroupsEnabled ? 'Invite more members' : 'Get started'}
+ {groupMembersCount > 0 ? 'Invite more members' : 'Get started'}
@@ -100,11 +109,6 @@ const NoBnEBudgetActivity = ({ openInviteModal, isEnterpriseGroupsEnabled }) =>
NoBnEBudgetActivity.propTypes = {
openInviteModal: PropTypes.func.isRequired,
- isEnterpriseGroupsEnabled: PropTypes.bool,
-};
-
-NoBnEBudgetActivity.defaultProps = {
- isEnterpriseGroupsEnabled: false,
};
export default NoBnEBudgetActivity;
diff --git a/src/components/learner-credit-management/invite-modal/InviteModalBudgetCard.jsx b/src/components/learner-credit-management/invite-modal/InviteModalBudgetCard.jsx
index e1e444f9e1..b19c27c8f7 100644
--- a/src/components/learner-credit-management/invite-modal/InviteModalBudgetCard.jsx
+++ b/src/components/learner-credit-management/invite-modal/InviteModalBudgetCard.jsx
@@ -25,7 +25,7 @@ const InviteModalBudgetCard = ({
const intl = useIntl();
const { subsidyAccessPolicyId, enterpriseOfferId } = useBudgetId();
const { data: subsidyAccessPolicy } = useSubsidyAccessPolicy(subsidyAccessPolicyId);
- const { data } = useEnterpriseGroupLearners(subsidyAccessPolicy.groupAssociations[0]);
+ const { data } = useEnterpriseGroupLearners(subsidyAccessPolicy?.groupAssociations[0]);
const memberSubtitle = data?.count ? `${makePlural(data?.count, 'current member')}` : '';
const budgetType = (enterpriseOfferId !== null) ? BUDGET_TYPES.ecommerce : BUDGET_TYPES.policy;
diff --git a/src/components/learner-credit-management/members-tab/tests/MembersTab.test.jsx b/src/components/learner-credit-management/members-tab/tests/MembersTab.test.jsx
index 913ca7ab58..46137ee5f3 100644
--- a/src/components/learner-credit-management/members-tab/tests/MembersTab.test.jsx
+++ b/src/components/learner-credit-management/members-tab/tests/MembersTab.test.jsx
@@ -17,9 +17,9 @@ import {
useBudgetDetailActivityOverview,
useEnterpriseGroupLearners,
useEnterpriseGroupMembersTableData,
- useSubsidySummaryAnalyticsApi,
useEnterpriseOffer,
useEnterpriseRemovedGroupMembers,
+ useSubsidySummaryAnalyticsApi,
} from '../../data';
import { EnterpriseSubsidiesContext } from '../../../EnterpriseSubsidiesContext';
import {
diff --git a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
index 7baee2407c..a2be5ab47c 100644
--- a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
+++ b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
@@ -24,6 +24,7 @@ import {
useEnterpriseCustomer,
useEnterpriseGroup,
useEnterpriseGroupLearners,
+ useEnterpriseRemovedGroupMembers,
useEnterpriseOffer,
useIsLargeOrGreater,
useSubsidyAccessPolicy,
@@ -66,6 +67,7 @@ jest.mock('../data', () => ({
useEnterpriseGroup: jest.fn(),
useEnterpriseGroupLearners: jest.fn(),
useEnterpriseGroupMembersTableData: jest.fn(),
+ useEnterpriseRemovedGroupMembers: jest.fn(),
useEnterpriseOffer: jest.fn(),
useIsLargeOrGreater: jest.fn().mockReturnValue(true),
useSubsidyAccessPolicy: jest.fn(),
@@ -453,6 +455,10 @@ describe('', () => {
budgetRedemptions: mockEmptyBudgetRedemptions,
fetchBudgetRedemptions: jest.fn(),
});
+ useEnterpriseRemovedGroupMembers.mockReturnValue({
+ isRemovedMembersLoading: false,
+ removedGroupMembersCount: 0,
+ });
renderWithRouter();
if (isLoading) {
@@ -648,12 +654,63 @@ describe('', () => {
budgetRedemptions: mockEmptyBudgetRedemptions,
fetchBudgetRedemptions: jest.fn(),
});
+ useEnterpriseRemovedGroupMembers.mockReturnValue({
+ isRemovedMembersLoading: false,
+ removedGroupMembersCount: 0,
+ });
renderWithRouter();
// Overview empty state (no content assignments, no spent transactions)
expect(screen.getByText('No budget activity yet? Invite members to browse the catalog and enroll!')).toBeInTheDocument();
const illustrationTestIds = ['name-your-members-illustration', 'members-browse-illustration', 'enroll-and-spend-illustration'];
illustrationTestIds.forEach(testId => expect(screen.getByTestId(testId)).toBeInTheDocument());
+ expect(screen.getByText('Get started', { selector: 'a' })).toBeInTheDocument();
+ });
+
+ it('still render bne zero state if there are members but no spend', async () => {
+ useParams.mockReturnValue({
+ enterpriseSlug: 'test-enterprise-slug',
+ enterpriseAppPage: 'test-enterprise-page',
+ budgetId: 'a52e6548-649f-4576-b73f-c5c2bee25e9c',
+ activeTabKey: 'activity',
+ });
+ useSubsidyAccessPolicy.mockReturnValue({
+ isInitialLoading: false,
+ data: mockPerLearnerSpendLimitSubsidyAccessPolicy,
+ });
+ useEnterpriseGroupLearners.mockReturnValue({
+ data: {
+ count: 1,
+ currentPage: 1,
+ next: null,
+ numPages: 1,
+ results: {
+ enterpriseGroupMembershipUuid: 'cde2e374-032f-4c08-8c0d-bf3205fa7c7e',
+ learnerId: 4382,
+ memberDetails: { userEmail: 'foobar@test.com', userName: 'ayy lmao' },
+ },
+ },
+ });
+ useEnterpriseRemovedGroupMembers.mockReturnValue({
+ isRemovedMembersLoading: false,
+ removedGroupMembersCount: 0,
+ });
+ useBudgetDetailActivityOverview.mockReturnValue({
+ isLoading: false,
+ data: mockEmptyStateBudgetDetailActivityOverview,
+ });
+ useBudgetRedemptions.mockReturnValue({
+ isLoading: false,
+ budgetRedemptions: mockEmptyBudgetRedemptions,
+ fetchBudgetRedemptions: jest.fn(),
+ });
+ renderWithRouter();
+
+ // Overview empty state (no content assignments, no spent transactions)
+ screen.debug(undefined, 1000000);
+
+ expect(screen.queryByText('No budget activity yet? Invite members to browse the catalog and enroll!')).toBeInTheDocument();
+
expect(screen.getByText('Invite more members', { selector: 'a' })).toBeInTheDocument();
});
@@ -1548,6 +1605,10 @@ describe('', () => {
budgetRedemptions: mockEmptyBudgetRedemptions,
fetchBudgetRedemptions: jest.fn(),
});
+ useEnterpriseRemovedGroupMembers.mockReturnValue({
+ isRemovedMembersLoading: false,
+ removedGroupMembersCount: 0,
+ });
renderWithRouter();
// Catalog tab does NOT exist
@@ -2228,6 +2289,10 @@ describe('', () => {
},
},
});
+ useEnterpriseRemovedGroupMembers.mockReturnValue({
+ isRemovedMembersLoading: false,
+ removedGroupMembersCount: 0,
+ });
renderWithRouter(