Files
frontend-app-authoring/src/library-authoring/component-info/ComponentManagement.tsx
Navin Karkera 3c22e4bbe1 feat: Add sidebar and library dropdown filter [FC-0114] (#2778)
* Add flow in course outline sidebar. Allows author to add new section/subsection/unit or any container from existing libraries via sidebar.
* Adds library dropdown filter and collections dropdown filter in add sidebar. Allows authors to filter containers by selected libraries and collections.
2026-01-09 12:14:48 -05:00

149 lines
5.2 KiB
TypeScript

import React, { useEffect } from 'react';
import { getConfig } from '@edx/frontend-platform';
import { useIntl } from '@edx/frontend-platform/i18n';
import { Collapsible, Icon, Stack } from '@openedx/paragon';
import {
BookOpen, ExpandLess, ExpandMore, Tag,
} from '@openedx/paragon/icons';
import { useOptionalLibraryContext } from '../common/context/LibraryContext';
import { SidebarActions, useSidebarContext } from '../common/context/SidebarContext';
import { ContentTagsDrawer, useContentTaxonomyTagsData } from '../../content-tags-drawer';
import { useLibraryBlockMetadata, useUpdateComponentCollections } from '../data/apiHooks';
import StatusWidget from '../generic/status-widget';
import { ManageCollections } from '../generic/manage-collections';
import messages from './messages';
const ComponentManagement = () => {
const intl = useIntl();
const { readOnly, isLoadingLibraryData } = useOptionalLibraryContext();
const { sidebarItemInfo, sidebarAction, resetSidebarAction } = useSidebarContext();
const jumpToCollections = sidebarAction === SidebarActions.JumpToManageCollections;
const jumpToTags = sidebarAction === SidebarActions.JumpToManageTags;
const [tagsCollapseIsOpen, setTagsCollapseOpen] = React.useState(!jumpToCollections);
const [collectionsCollapseIsOpen, setCollectionsCollapseOpen] = React.useState(true);
useEffect(() => {
if (jumpToCollections) {
setTagsCollapseOpen(false);
setCollectionsCollapseOpen(true);
} else if (jumpToTags) {
setTagsCollapseOpen(true);
setCollectionsCollapseOpen(false);
}
}, [jumpToCollections, jumpToTags]);
useEffect(() => {
// This is required to redo actions.
if (tagsCollapseIsOpen || !collectionsCollapseIsOpen) {
resetSidebarAction();
}
}, [tagsCollapseIsOpen, collectionsCollapseIsOpen]);
const usageKey = sidebarItemInfo?.id;
// istanbul ignore if: this should never happen
if (!usageKey) {
throw new Error('usageKey is required');
}
const { data: componentMetadata } = useLibraryBlockMetadata(usageKey);
const { data: componentTags } = useContentTaxonomyTagsData(usageKey);
const collectionsCount = React.useMemo(() => componentMetadata?.collections?.length || 0, [componentMetadata]);
const tagsCount = React.useMemo(() => {
if (!componentTags) {
return 0;
}
let result = 0;
componentTags.taxonomies.forEach((taxonomy) => {
const countedTags : string[] = [];
taxonomy.tags.forEach((tagData) => {
tagData.lineage.forEach((tag) => {
if (!countedTags.includes(tag)) {
result += 1;
countedTags.push(tag);
}
});
});
});
return result;
}, [componentTags]);
// istanbul ignore if: this should never happen
if (isLoadingLibraryData) {
return null;
}
// istanbul ignore if: this should never happen
if (!componentMetadata) {
return null;
}
return (
<Stack gap={3}>
<StatusWidget
{...componentMetadata}
/>
{[true, 'true'].includes(getConfig().ENABLE_TAGGING_TAXONOMY_PAGES)
&& (
<Collapsible.Advanced
open={tagsCollapseIsOpen}
className="collapsible-card border-0"
>
<Collapsible.Trigger
onClick={() => setTagsCollapseOpen((prev) => !prev)}
className="collapsible-trigger d-flex justify-content-between p-2"
>
<Stack gap={1} direction="horizontal">
<Icon src={Tag} />
{intl.formatMessage(messages.manageTabTagsTitle, { count: tagsCount })}
</Stack>
<Collapsible.Visible whenClosed>
<Icon src={ExpandMore} />
</Collapsible.Visible>
<Collapsible.Visible whenOpen>
<Icon src={ExpandLess} />
</Collapsible.Visible>
</Collapsible.Trigger>
<Collapsible.Body className="collapsible-body">
<ContentTagsDrawer
id={usageKey}
variant="component"
readOnly={readOnly}
/>
</Collapsible.Body>
</Collapsible.Advanced>
)}
<Collapsible.Advanced
open={collectionsCollapseIsOpen}
className="collapsible-card border-0"
>
<Collapsible.Trigger
onClick={() => setCollectionsCollapseOpen((prev) => !prev)}
className="collapsible-trigger d-flex justify-content-between p-2"
>
<Stack gap={1} direction="horizontal">
<Icon src={BookOpen} />
{intl.formatMessage(messages.manageTabCollectionsTitle, { count: collectionsCount })}
</Stack>
<Collapsible.Visible whenClosed>
<Icon src={ExpandMore} />
</Collapsible.Visible>
<Collapsible.Visible whenOpen>
<Icon src={ExpandLess} />
</Collapsible.Visible>
</Collapsible.Trigger>
<Collapsible.Body className="collapsible-body">
<ManageCollections
opaqueKey={usageKey}
collections={componentMetadata.collections}
useUpdateCollectionsHook={useUpdateComponentCollections}
/>
</Collapsible.Body>
</Collapsible.Advanced>
</Stack>
);
};
export default ComponentManagement;