diff --git a/packages/common/index.ts b/packages/common/index.ts index 319b935a5..2cd088932 100644 --- a/packages/common/index.ts +++ b/packages/common/index.ts @@ -1753,6 +1753,7 @@ export class LMSCourseIntegrationPartial { lmsSynchronize!: boolean isExpired!: boolean selectedResourceTypes?: LMSResourceType[] + moduleLinkedPagesOnly?: boolean } export type LMSCourseAPIResponse = { @@ -1790,6 +1791,7 @@ export type LMSPage = { syncEnabled?: boolean modified?: Date uploaded?: Date + isModuleLinked?: boolean } export type LMSFile = { @@ -1803,6 +1805,23 @@ export type LMSFile = { uploaded?: Date } +export type LMSModule = { + id: number + name: string + items_url?: string + items?: LMSModuleItem[] +} + +export type LMSModuleItem = { + id: number + title: string + type: string + content_id?: number + html_url?: string + url?: string + page_url?: string +} + export enum LMSQuizAccessLevel { LOGISTICS_ONLY = 'logistics_only', LOGISTICS_AND_QUESTIONS = 'logistics_and_questions', diff --git a/packages/frontend/app/(dashboard)/course/[cid]/(settings)/settings/lms_integrations/page.tsx b/packages/frontend/app/(dashboard)/course/[cid]/(settings)/settings/lms_integrations/page.tsx index e05bb8361..b0f116636 100644 --- a/packages/frontend/app/(dashboard)/course/[cid]/(settings)/settings/lms_integrations/page.tsx +++ b/packages/frontend/app/(dashboard)/course/[cid]/(settings)/settings/lms_integrations/page.tsx @@ -127,12 +127,17 @@ export default function CourseLMSIntegrationPage(props: { const [delModalOpen, setDelModalOpen] = useState(false) const [isTesting, setIsTesting] = useState(false) const [selectedResources, setSelectedResources] = useState([]) + const [moduleLinkedPagesOnly, setModuleLinkedPagesOnly] = + useState(false) useEffect(() => { if (integration?.selectedResourceTypes) { setSelectedResources(integration.selectedResourceTypes) } - }, [integration?.selectedResourceTypes]) + if (integration?.moduleLinkedPagesOnly !== undefined) { + setModuleLinkedPagesOnly(integration.moduleLinkedPagesOnly) + } + }, [integration?.selectedResourceTypes, integration?.moduleLinkedPagesOnly]) const onSelectedResourcesChange: GetProp< typeof Checkbox.Group, @@ -145,9 +150,18 @@ export default function CourseLMSIntegrationPage(props: { if (!integration?.selectedResourceTypes) return false const currentSorted = [...selectedResources].sort() const dbSorted = [...integration.selectedResourceTypes].sort() - if (currentSorted.length !== dbSorted.length) return true - return currentSorted.some((item, index) => item !== dbSorted[index]) - }, [selectedResources, integration?.selectedResourceTypes]) + const resourcesChanged = + currentSorted.length !== dbSorted.length || + currentSorted.some((item, index) => item !== dbSorted[index]) + const moduleLinkedChanged = + moduleLinkedPagesOnly !== integration.moduleLinkedPagesOnly + return resourcesChanged || moduleLinkedChanged + }, [ + selectedResources, + integration?.selectedResourceTypes, + moduleLinkedPagesOnly, + integration?.moduleLinkedPagesOnly, + ]) const fetchOrgIntegrationsAsync = useCallback(async () => { await API.lmsIntegration @@ -286,16 +300,42 @@ export default function CourseLMSIntegrationPage(props: { } const handleSaveAndResync = async () => { - try { - await API.lmsIntegration.updateSelectedResourceTypes( - courseId, - selectedResources as string[], - ) - message.success('Resource types updated!') - await forceSync() - setUpdateFlag(!updateFlag) - } catch (err) { - message.error(getErrorMessage(err)) + const wasModuleLinkedDisabled = !integration?.moduleLinkedPagesOnly + const isEnablingModuleLinked = + wasModuleLinkedDisabled && moduleLinkedPagesOnly + + const performUpdate = async () => { + try { + await API.lmsIntegration.updateSelectedResourceTypes( + courseId, + selectedResources as string[], + ) + + await API.lmsIntegration.updateModuleLinkedPagesOnly( + courseId, + moduleLinkedPagesOnly, + ) + + message.success('Settings updated!') + await forceSync() + setUpdateFlag(!updateFlag) + } catch (err) { + message.error(getErrorMessage(err)) + } + } + + if (isEnablingModuleLinked) { + Modal.confirm({ + title: 'Enable Module-linked Pages Only?', + content: + 'Enabling "Module-linked pages only" will remove any standalone pages (not linked in course modules) from the chatbot. ' + + 'Only pages that are specifically linked within course modules will remain available to the chatbot.', + okText: 'Continue', + cancelText: 'Cancel', + onOk: performUpdate, + }) + } else { + await performUpdate() } } @@ -810,43 +850,78 @@ export default function CourseLMSIntegrationPage(props: { label: 'Resource Selector', key: '2', children: ( -
+
- - - - Assignments - + + +
+ + Assignments + +
- - - Announcements - + +
+ + Announcements + +
- - Files + +
+ Files +
- - Pages + +
+ Pages +
- - Quizzes + +
+ Quizzes +
- - - - - Syllabus - - - + +
+ + + + Syllabus + + + +
+ + {/* Module-linked pages toggle */} + {selectedResources.includes('pages') && ( +
+ { + setModuleLinkedPagesOnly(e.target.checked) + }} + > + + + Module-linked pages only + + + +
+ )} +