import React, { useCallback, useEffect, useMemo, useState, } from 'react'; import { Helmet } from 'react-helmet'; import { getConfig } from '@edx/frontend-platform'; import { FormattedMessage, useIntl } from '@edx/frontend-platform/i18n'; import { Alert, ActionRow, Button, Card, Container, Hyperlink, Icon, Stack, Tab, Tabs, } from '@openedx/paragon'; import { Cached, CheckCircle, Launch, Loop, } from '@openedx/paragon/icons'; import sumBy from 'lodash/sumBy'; import { useSearchParams } from 'react-router-dom'; import getPageHeadTitle from '../generic/utils'; import { useModel } from '../generic/model-store'; import messages from './messages'; import SubHeader from '../generic/sub-header/SubHeader'; import { useEntityLinksSummaryByDownstreamContext } from './data/apiHooks'; import type { PublishableEntityLinkSummary } from './data/api'; import Loading from '../generic/Loading'; import { useStudioHome } from '../studio-home/hooks'; import NewsstandIcon from '../generic/NewsstandIcon'; import ReviewTabContent from './ReviewTabContent'; import { OutOfSyncAlert } from './OutOfSyncAlert'; interface Props { courseId: string; } interface LibraryCardProps { linkSummary: PublishableEntityLinkSummary; } export enum CourseLibraryTabs { all = 'all', review = 'review', } const LibraryCard = ({ linkSummary }: LibraryCardProps) => { const intl = useIntl(); return ( {linkSummary.upstreamContextTitle} )} actions={( )} size="sm" /> {intl.formatMessage(messages.totalComponentLabel, { totalComponents: linkSummary.totalCount })} {linkSummary.readyToSyncCount > 0 && ( {intl.formatMessage(messages.outOfSyncCountLabel, { outOfSyncCount: linkSummary.readyToSyncCount })} )} ); }; export const CourseLibraries: React.FC = ({ courseId }) => { const intl = useIntl(); const courseDetails = useModel('courseDetails', courseId); const [searchParams] = useSearchParams(); const [tabKey, setTabKey] = useState( () => searchParams.get('tab') as CourseLibraryTabs, ); const [showReviewAlert, setShowReviewAlert] = useState(false); const { data: libraries, isLoading } = useEntityLinksSummaryByDownstreamContext(courseId); const outOfSyncCount = useMemo(() => sumBy(libraries, (lib) => lib.readyToSyncCount), [libraries]); const { isLoadingPage: isLoadingStudioHome, isFailedLoadingPage: isFailedLoadingStudioHome, librariesV2Enabled, } = useStudioHome(); const onAlertReview = () => { setTabKey(CourseLibraryTabs.review); }; const tabChange = useCallback((selectedTab: CourseLibraryTabs) => { setTabKey(selectedTab); }, []); useEffect(() => { setTabKey((prev) => { if (outOfSyncCount > 0) { return CourseLibraryTabs.review; } if (prev) { return prev; } /* istanbul ignore next */ return CourseLibraryTabs.all; }); }, [outOfSyncCount]); const renderLibrariesTabContent = useCallback(() => { if (isLoading) { return ; } if (libraries?.length === 0) { return ; } return ( <> {libraries?.map((library) => ( ))} ); }, [libraries, isLoading]); const renderReviewTabContent = useCallback(() => { if (isLoading) { return ; } if (tabKey !== CourseLibraryTabs.review) { return null; } if (!outOfSyncCount) { return ( ); } return ; }, [outOfSyncCount, isLoading, tabKey]); if (!isLoadingStudioHome && (!librariesV2Enabled || isFailedLoadingStudioHome)) { return ( {intl.formatMessage(messages.librariesV2DisabledError)} ); } return ( <> {getPageHeadTitle(courseDetails?.name, intl.formatMessage(messages.headingTitle))} 0 && tabKey === CourseLibraryTabs.all && ( )} hideBorder />
{renderLibrariesTabContent()} {intl.formatMessage(messages.reviewTabTitle)} )} notification={outOfSyncCount} className="px-2 mt-3" > {renderReviewTabContent()}
); };