diff --git a/src/components/ContentHighlights/ContentHighlightArchivedAlert.jsx b/src/components/ContentHighlights/ContentHighlightArchivedAlert.jsx
index 4f899e08b2..bd126a9002 100644
--- a/src/components/ContentHighlights/ContentHighlightArchivedAlert.jsx
+++ b/src/components/ContentHighlights/ContentHighlightArchivedAlert.jsx
@@ -3,7 +3,8 @@ import { useContext, useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { NEW_ARCHIVED_CONTENT_ALERT_DISMISSED_COOKIE_NAME } from './data/constants';
import { EnterpriseAppContext } from '../EnterpriseApp/EnterpriseAppContextProvider';
-import { enterpriseCurationActions, isArchivedContent } from '../EnterpriseApp/data/enterpriseCurationReducer';
+import { enterpriseCurationActions } from '../EnterpriseApp/data/enterpriseCurationReducer';
+import { isArchivedContent } from '../../utils';
const ContentHighlightArchivedAlert = ({ open, onClose }) => {
const [archivedContentLocalStorage, setArchivedContentLocalStorage] = useState({});
diff --git a/src/components/ContentHighlights/ContentHighlightSetCard.jsx b/src/components/ContentHighlights/ContentHighlightSetCard.jsx
index a0357e8085..1a731ae0b2 100644
--- a/src/components/ContentHighlights/ContentHighlightSetCard.jsx
+++ b/src/components/ContentHighlights/ContentHighlightSetCard.jsx
@@ -6,6 +6,7 @@ import { useNavigate } from 'react-router-dom';
import { ROUTE_NAMES } from '../EnterpriseApp/data/constants';
import { useContentHighlightsContext } from './data/hooks';
+import { makePlural } from '../../utils';
const ContentHighlightSetCard = ({
imageCapSrc,
@@ -14,6 +15,7 @@ const ContentHighlightSetCard = ({
isPublished,
enterpriseSlug,
itemCount,
+ archivedItemCount,
onClick,
}) => {
const navigate = useNavigate();
@@ -30,6 +32,15 @@ const ContentHighlightSetCard = ({
openStepperModal();
};
+ const cardItemText = () => {
+ let returnString = '';
+ returnString += makePlural(itemCount, 'item');
+ if (archivedItemCount > 0) {
+ returnString += ` : ${makePlural(archivedItemCount, 'archived item')}`;
+ }
+ return returnString;
+ };
+
return (
- {itemCount} items
+ {cardItemText()}
);
@@ -51,6 +62,7 @@ ContentHighlightSetCard.propTypes = {
enterpriseSlug: PropTypes.string.isRequired,
isPublished: PropTypes.bool.isRequired,
itemCount: PropTypes.number.isRequired,
+ archivedItemCount: PropTypes.number.isRequired,
imageCapSrc: PropTypes.string.isRequired,
onClick: PropTypes.func.isRequired,
};
diff --git a/src/components/ContentHighlights/ContentHighlightsCardItemsContainer.jsx b/src/components/ContentHighlights/ContentHighlightsCardItemsContainer.jsx
index 9e72f98322..802809e67c 100644
--- a/src/components/ContentHighlights/ContentHighlightsCardItemsContainer.jsx
+++ b/src/components/ContentHighlights/ContentHighlightsCardItemsContainer.jsx
@@ -7,7 +7,6 @@ import { connect } from 'react-redux';
import { sendEnterpriseTrackEvent } from '@edx/frontend-enterprise-utils';
import ContentHighlightCardItem from './ContentHighlightCardItem';
import {
- COURSE_RUN_STATUSES,
DEFAULT_ERROR_MESSAGE,
HIGHLIGHTS_CARD_GRID_COLUMN_SIZES,
MAX_CONTENT_ITEMS_PER_HIGHLIGHT_SET,
@@ -17,6 +16,7 @@ import { generateAboutPageUrl } from './data/utils';
import EVENT_NAMES from '../../eventTracking';
import { features } from '../../config';
import DeleteArchivedHighlightsDialogs from './DeleteArchivedHighlightsDialogs';
+import { isArchivedContent } from '../../utils';
const ContentHighlightsCardItemsContainer = ({
enterpriseId, enterpriseSlug, isLoading, highlightedContent, updateHighlightSet,
@@ -44,16 +44,8 @@ const ContentHighlightsCardItemsContainer = ({
if (FEATURE_HIGHLIGHTS_ARCHIVE_MESSAGING) {
for (let i = 0; i < highlightedContent.length; i++) {
- const {
- courseRunStatuses,
- } = highlightedContent[i];
- if (courseRunStatuses) {
- // a course is only archived if all of its runs are archived
- if (courseRunStatuses?.every(status => status === COURSE_RUN_STATUSES.archived)) {
- archivedContent.push(highlightedContent[i]);
- } else {
- activeContent.push(highlightedContent[i]);
- }
+ if (isArchivedContent(highlightedContent[i])) {
+ archivedContent.push(highlightedContent[i]);
} else {
activeContent.push(highlightedContent[i]);
}
diff --git a/src/components/ContentHighlights/HighlightSetSection.jsx b/src/components/ContentHighlights/HighlightSetSection.jsx
index c9cb79128e..1fa04b5b60 100644
--- a/src/components/ContentHighlights/HighlightSetSection.jsx
+++ b/src/components/ContentHighlights/HighlightSetSection.jsx
@@ -40,6 +40,7 @@ const HighlightSetSection = ({
isPublished,
highlightedContentUuids,
cardImageUrl,
+ archivedContentCount,
}) => (
trackClickEvent({
uuid, title, isPublished, highlightedContentUuids,
diff --git a/src/components/EnterpriseApp/data/enterpriseCurationReducer.js b/src/components/EnterpriseApp/data/enterpriseCurationReducer.js
index 08d2a45ebc..6e1a0eee20 100644
--- a/src/components/EnterpriseApp/data/enterpriseCurationReducer.js
+++ b/src/components/EnterpriseApp/data/enterpriseCurationReducer.js
@@ -1,5 +1,6 @@
import { logError } from '@edx/frontend-platform/logging';
-import { NEW_ARCHIVED_CONTENT_ALERT_DISMISSED_COOKIE_NAME, COURSE_RUN_STATUSES } from '../../ContentHighlights/data/constants';
+import { NEW_ARCHIVED_CONTENT_ALERT_DISMISSED_COOKIE_NAME } from '../../ContentHighlights/data/constants';
+import { isArchivedContent } from '../../../utils';
export const initialReducerState = {
isLoading: true,
@@ -71,23 +72,6 @@ function getHighlightSetsFromState(state) {
return state.enterpriseCuration?.highlightSets || [];
}
-/**
- * Helper function to determine if a content is archived.
- *
- * @param {Object} content
- * @returns {Boolean}
- */
-export function isArchivedContent(content) {
- const { courseRunStatuses } = content;
-
- if (!courseRunStatuses) {
- return false;
- }
-
- const ARCHIVABLE_STATUSES = [COURSE_RUN_STATUSES.archived, COURSE_RUN_STATUSES.unpublished];
- return courseRunStatuses.every(status => ARCHIVABLE_STATUSES.includes(status));
-}
-
/**
* Helper function to determine if there is a new archived content in a highlight set
*
diff --git a/src/components/settings/SettingsLMSTab/ExistingCard.jsx b/src/components/settings/SettingsLMSTab/ExistingCard.jsx
index 1f8a73f720..28b78d26de 100644
--- a/src/components/settings/SettingsLMSTab/ExistingCard.jsx
+++ b/src/components/settings/SettingsLMSTab/ExistingCard.jsx
@@ -32,7 +32,7 @@ const ExistingCard = ({
getStatus,
}) => {
const location = useLocation();
- const pathname = location.pathname;
+ const { pathname } = location;
const redirectPath = pathname.endsWith('/') ? pathname : `${pathname}/`;
const [showDeleteModal, setShowDeleteModal] = useState(false);
const isEdxStaff = getAuthenticatedUser().administrator;
diff --git a/src/data/services/EnterpriseDataApiService.js b/src/data/services/EnterpriseDataApiService.js
index b047d6329a..e64e860a80 100644
--- a/src/data/services/EnterpriseDataApiService.js
+++ b/src/data/services/EnterpriseDataApiService.js
@@ -2,7 +2,7 @@ import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import { snakeCaseObject } from '@edx/frontend-platform/utils';
import store from '../store';
-import { configuration, features } from '../../config';
+import { configuration } from '../../config';
class EnterpriseDataApiService {
// TODO: This should access the data-api through the gateway instead of direct
diff --git a/src/utils.js b/src/utils.js
index 9678faffcc..f5dc8840ab 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -24,6 +24,7 @@ import CornerstoneIcon from './icons/CSOD.png';
import DegreedIcon from './icons/Degreed.png';
import MoodleIcon from './icons/Moodle.png';
import SAPIcon from './icons/SAP.svg';
+import { COURSE_RUN_STATUSES } from './components/ContentHighlights/data/constants';
import LmsApiService from './data/services/LmsApiService';
@@ -447,6 +448,37 @@ function getActiveTableColumnFilters(columns) {
})).filter(filter => !!filter.filterValue);
}
+/**
+ * Helper to transform a string into a plural form based on a number.
+ *
+ * @returns A string with the number and the plural form of the string.
+ */
+function makePlural(num, string) {
+ const stringEndings = ['s', 'x', 'z'];
+ if (num > 1 || num === 0) {
+ if (stringEndings.includes(string.charAt(string.length - 1))) {
+ return `${num} ${string}es`;
+ }
+ return `${num} ${string}s`;
+ }
+ return `${num} ${string}`;
+}
+
+/**
+ * Helper function to determine if a content is archived.
+ *
+ * @param {Object} content (can be program, course, or pathway)
+ * @returns {Boolean}
+ */
+function isArchivedContent(content) {
+ const { courseRunStatuses } = content;
+ if (!courseRunStatuses) {
+ return false;
+ }
+ const ARCHIVABLE_STATUSES = [COURSE_RUN_STATUSES.archived, COURSE_RUN_STATUSES.unpublished];
+ return courseRunStatuses.every(status => ARCHIVABLE_STATUSES.includes(status));
+}
+
export {
camelCaseDict,
camelCaseDictArray,
@@ -484,4 +516,6 @@ export {
isAssignableSubsidyAccessPolicyType,
getActiveTableColumnFilters,
queryCacheOnErrorHandler,
+ makePlural,
+ isArchivedContent,
};