1- import { useSelector } from 'react-redux' ;
1+ import { useEffect } from 'react' ;
2+ import { useDispatch , useSelector } from 'react-redux' ;
23import { useIntl } from '@edx/frontend-platform/i18n' ;
34import { useNavigate } from 'react-router-dom' ;
45import { Tag } from '@openedx/paragon/icons' ;
56import { useQueryClient } from '@tanstack/react-query' ;
7+ import { useToggle } from '@openedx/paragon' ;
68
79import { SidebarContent , SidebarSection , SidebarTitle } from '@src/generic/sidebar' ;
810import { ContentTagsSnippet } from '@src/content-tags-drawer' ;
9- import { useContentData } from '@src/content-tags-drawer/data/apiHooks' ;
10- import type { XBlockData } from '@src/content-tags-drawer/data/types' ;
1111import { getItemIcon } from '@src/generic/block-type-utils' ;
1212import { useIframe } from '@src/generic/hooks/context/hooks' ;
1313import { messageTypes } from '@src/course-unit/constants' ;
1414import { LibraryReferenceCard } from '@src/generic/library-reference-card/LibraryReferenceCard' ;
15- import { getCourseUnitData } from '@src/course-unit/data/selectors' ;
15+ import { getCourseUnitData , getMovedXBlockParams } from '@src/course-unit/data/selectors' ;
1616import { useCourseAuthoringContext } from '@src/CourseAuthoringContext' ;
17- import { courseOutlineQueryKeys } from '@src/course-outline/data/apiHooks' ;
17+ import { courseOutlineQueryKeys , useCourseItemData } from '@src/course-outline/data/apiHooks' ;
18+ import { getLibraryId } from '@src/generic/key-utils' ;
19+ import { useUnlinkDownstream , UnlinkModal } from '@src/generic/unlink-modal' ;
20+ import { useClipboard } from '@src/generic/clipboard' ;
21+ import DeleteModal from '@src/generic/delete-modal/DeleteModal' ;
22+ import { deleteUnitItemQuery , duplicateUnitItemQuery , fetchCourseVerticalChildrenData } from '@src/course-unit/data/thunk' ;
1823
1924import { useUnitSidebarContext } from '../UnitSidebarContext' ;
2025import messages from './messages' ;
@@ -26,19 +31,31 @@ export const ComponentInfoSidebar = () => {
2631 const intl = useIntl ( ) ;
2732 const queryClient = useQueryClient ( ) ;
2833 const navigate = useNavigate ( ) ;
34+ const dispatch = useDispatch ( ) ;
2935 const { sendMessageToIframe } = useIframe ( ) ;
36+ const { copyToClipboard } = useClipboard ( ) ;
3037 const unitData = useSelector ( getCourseUnitData ) ;
3138 const { courseId } = useCourseAuthoringContext ( ) ;
3239 const sectionId = unitData ?. ancestorInfo ?. ancestors ?. find (
3340 ( ancestor ) => ancestor . category === 'chapter' ,
3441 ) ?. id ;
42+ const [ isUnlinkModalOpen , openUnlinkModal , closeUnlinkModal ] = useToggle ( false ) ;
43+ const [ isDeleteModalOpen , openDeleteModal , closeDeleteModal ] = useToggle ( false ) ;
3544
3645 const {
3746 selectedComponentId,
3847 setCurrentPageKey,
3948 } = useUnitSidebarContext ( ) ;
49+ const { mutateAsync : unlinkDownstream } = useUnlinkDownstream ( ) ;
4050
41- const { data : contentData } = useContentData ( selectedComponentId ) as { data : XBlockData | undefined } ;
51+ const { data : componentItemData } = useCourseItemData ( selectedComponentId ?? undefined ) ;
52+ const movedXBlockParams = useSelector ( getMovedXBlockParams ) ;
53+
54+ useEffect ( ( ) => {
55+ if ( movedXBlockParams . isSuccess && movedXBlockParams . sourceLocator === selectedComponentId ) {
56+ setCurrentPageKey ( 'info' , null ) ;
57+ }
58+ } , [ movedXBlockParams ] ) ;
4259
4360 // istanbul ignore next
4461 const handleBack = ( ) => {
@@ -57,21 +74,92 @@ export const ComponentInfoSidebar = () => {
5774 } ) ;
5875 } ;
5976
77+ const handleDuplicate = ( ) => {
78+ if ( selectedComponentId && unitData ?. id ) {
79+ dispatch ( duplicateUnitItemQuery (
80+ unitData . id ,
81+ selectedComponentId ,
82+ ( courseKey : string , locator : string ) => sendMessageToIframe (
83+ messageTypes . completeXBlockDuplicating ,
84+ { courseKey, locator } ,
85+ ) ,
86+ ) ) ;
87+ }
88+ } ;
89+
90+ const handleDeleteSubmit = async ( ) => {
91+ if ( selectedComponentId && unitData ?. id ) {
92+ await dispatch ( deleteUnitItemQuery ( unitData . id , selectedComponentId , sendMessageToIframe ) ) ;
93+ closeDeleteModal ( ) ;
94+ setCurrentPageKey ( 'info' , null ) ;
95+ if ( unitData ?. id ) {
96+ dispatch ( fetchCourseVerticalChildrenData ( unitData . id , false ) ) ;
97+ }
98+ }
99+ } ;
100+
101+ const handleUnlinkSubmit = async ( ) => {
102+ if ( selectedComponentId ) {
103+ await unlinkDownstream ( {
104+ downstreamBlockId : selectedComponentId ,
105+ } , {
106+ onSuccess : ( ) => {
107+ closeUnlinkModal ( ) ;
108+ queryClient . invalidateQueries ( {
109+ queryKey : courseOutlineQueryKeys . courseItemId ( selectedComponentId ) ,
110+ } ) ;
111+ if ( unitData ?. id ) {
112+ dispatch ( fetchCourseVerticalChildrenData ( unitData . id , false ) ) ;
113+ }
114+ } ,
115+ } ) ;
116+ }
117+ } ;
118+
119+ const handleMove = ( ) => {
120+ if ( ! selectedComponentId || ! unitData ?. id ) {
121+ return ;
122+ }
123+ window . dispatchEvent ( new MessageEvent ( 'message' , {
124+ data : {
125+ type : messageTypes . showMoveXBlockModal ,
126+ payload : {
127+ sourceXBlockInfo : {
128+ id : selectedComponentId ,
129+ displayName : componentItemData ?. displayName ?? '' ,
130+ category : componentItemData ?. category ?? '' ,
131+ } ,
132+ sourceParentXBlockInfo : {
133+ id : unitData . id ,
134+ displayName : unitData . displayName ?? '' ,
135+ category : 'vertical' ,
136+ } ,
137+ } ,
138+ } ,
139+ } ) ) ;
140+ } ;
141+
60142 return (
61143 < >
62144 < SidebarTitle
63- title = { contentData ?. displayName || '' }
64- icon = { getItemIcon ( contentData ?. category || '' ) }
145+ title = { componentItemData ?. displayName || '' }
146+ icon = { getItemIcon ( componentItemData ?. category || '' ) }
65147 onBackBtnClick = { handleBack }
66148 menuProps = { {
67149 itemId : selectedComponentId || '' ,
68150 index : 0 ,
69- onClickDuplicate : ( ) => { } ,
70- onClickUnlink : ( ) => { } ,
71- onClickDelete : ( ) => { } ,
72- onClickViewLibrary : ( ) => { } ,
73- onClickCopy : ( ) => { } ,
74- onClickMove : ( ) => { } ,
151+ onClickDuplicate : handleDuplicate ,
152+ onClickUnlink : openUnlinkModal ,
153+ onClickDelete : openDeleteModal ,
154+ onClickViewLibrary : ( ) => {
155+ const upstreamRef = componentItemData ?. upstreamInfo ?. upstreamRef ;
156+ if ( upstreamRef ) {
157+ const libId = getLibraryId ( upstreamRef ) ;
158+ navigate ( `/library/${ libId } /components/${ upstreamRef } ` ) ;
159+ }
160+ } ,
161+ onClickCopy : ( ) => copyToClipboard ( selectedComponentId ?? '' ) ,
162+ onClickMove : handleMove ,
75163 } }
76164 />
77165 < LibraryReferenceCard
@@ -88,6 +176,19 @@ export const ComponentInfoSidebar = () => {
88176 < ContentTagsSnippet contentId = { selectedComponentId || '' } />
89177 </ SidebarSection >
90178 </ SidebarContent >
179+ < UnlinkModal
180+ isOpen = { isUnlinkModalOpen }
181+ close = { closeUnlinkModal }
182+ onUnlinkSubmit = { handleUnlinkSubmit }
183+ displayName = { componentItemData ?. displayName }
184+ category = "vertical"
185+ />
186+ < DeleteModal
187+ isOpen = { isDeleteModalOpen }
188+ close = { closeDeleteModal }
189+ category = { componentItemData ?. category }
190+ onDeleteSubmit = { handleDeleteSubmit }
191+ />
91192 </ >
92193 ) ;
93194} ;
0 commit comments