diff --git a/src/optimizer-page/CourseOptimizerPage.test.js b/src/optimizer-page/CourseOptimizerPage.test.js index 5f69e1840..6e364ea95 100644 --- a/src/optimizer-page/CourseOptimizerPage.test.js +++ b/src/optimizer-page/CourseOptimizerPage.test.js @@ -143,6 +143,7 @@ describe('CourseOptimizerPage', () => { await waitFor(() => { expect(getAllByText(scanResultsMessages.brokenLinkStatus.defaultMessage)[0]).toBeInTheDocument(); expect(queryAllByText(scanResultsMessages.lockedLinkStatus.defaultMessage)[0]).toBeInTheDocument(); + expect(queryAllByText(scanResultsMessages.recommendedManualCheckText.defaultMessage)[0]).toBeInTheDocument(); }); }); diff --git a/src/optimizer-page/mocks/mockApiResponse.js b/src/optimizer-page/mocks/mockApiResponse.js index dd3b54e39..4d425c6e2 100644 --- a/src/optimizer-page/mocks/mockApiResponse.js +++ b/src/optimizer-page/mocks/mockApiResponse.js @@ -26,6 +26,7 @@ const mockApiResponse = { url: 'https://example.com/intro-guide', brokenLinks: ['https://example.com/broken-link-algo'], lockedLinks: ['https://example.com/locked-link-algo'], + externalForbiddenLinks: ['https://outsider.com/forbidden-link-algo'], }, ], }, @@ -92,6 +93,7 @@ const mockApiResponse = { url: 'https://example.com/broken-link-algo', brokenLinks: ['https://example.com/broken-link-algo'], lockedLinks: ['https://example.com/locked-link-algo'], + externalForbiddenLinks: ['https://outsider.com/forbidden-link-algo'], }, ], }, diff --git a/src/optimizer-page/scan-results/BrokenLinkTable.tsx b/src/optimizer-page/scan-results/BrokenLinkTable.tsx index ebd177d12..6f15462eb 100644 --- a/src/optimizer-page/scan-results/BrokenLinkTable.tsx +++ b/src/optimizer-page/scan-results/BrokenLinkTable.tsx @@ -1,5 +1,9 @@ -import { Icon, Table } from '@openedx/paragon'; -import { OpenInNew, Lock, LinkOff } from '@openedx/paragon/icons'; +import { + Card, Icon, OverlayTrigger, Table, Tooltip, +} from '@openedx/paragon'; +import { + OpenInNew, Lock, LinkOff, InfoOutline, +} from '@openedx/paragon/icons'; import { useIntl } from '@edx/frontend-platform/i18n'; import { FC } from 'react'; import { Unit } from '../types'; @@ -23,6 +27,26 @@ const GoToBlock: FC<{ block: { url: string } }> = ({ block }) => ( ); +const RecommendedManualCheckHeading = () => { + const intl = useIntl(); + return ( + + {intl.formatMessage(messages.recommendedManualCheckText)} + + {intl.formatMessage(messages.recommendedManualCheckTooltip)} + + )} + > + + + + ); +}; + interface BrokenLinkTableProps { unit: Unit; showLockedLinks: boolean; @@ -40,7 +64,7 @@ const BrokenLinkTable: FC = ({ }) => { const intl = useIntl(); return ( - <> +

{unit.displayName}

= ({ ), })); acc.push(...blockBrokenLinks); - if (!showLockedLinks) { - return acc; + + if (showLockedLinks) { + const blockLockedLinks = block.lockedLinks.map((link) => ({ + blockLink: , + blockDisplayName: block.displayName || '', + brokenLink: , + status: ( + + + {intl.formatMessage(messages.lockedLinkStatus)}{' '} + + + ), + })); + + acc.push(...blockLockedLinks); + } + + if (block.externalForbiddenLinks?.length > 0) { + const recommendedManualCheckHeading = { + blockLink:
, + blockDisplayName: , + brokenLink:
, + status:
, + }; + const externalForbiddenLinks = block.externalForbiddenLinks.map((link) => ({ + blockLink: , + blockDisplayName: block.displayName || '', + brokenLink: , + status:
, + })); + + acc.push(recommendedManualCheckHeading); + acc.push(...externalForbiddenLinks); } - const blockLockedLinks = block.lockedLinks.map((link) => ({ - blockLink: , - blockDisplayName: block.displayName || '', - brokenLink: , - status: ( - - - {intl.formatMessage(messages.lockedLinkStatus)}{' '} - - - ), - })); - acc.push(...blockLockedLinks); return acc; }, [], @@ -110,7 +153,7 @@ const BrokenLinkTable: FC = ({ }, ]} /> - + ); }; diff --git a/src/optimizer-page/scan-results/ScanResults.scss b/src/optimizer-page/scan-results/ScanResults.scss index 1918ad7b0..b17b3cbaa 100644 --- a/src/optimizer-page/scan-results/ScanResults.scss +++ b/src/optimizer-page/scan-results/ScanResults.scss @@ -41,6 +41,15 @@ background-color: $dark-100; padding: 10px; margin-bottom: 10px; + + &:not(:first-child){ + margin-top: 1rem; + } + } + + .unit-card{ + border: 1px solid #BCBCBC; + box-shadow: 0 1px 2px rgb(0 0 0 / .15); } /* Subsection Header */ @@ -49,7 +58,7 @@ margin-top: 10px; font-size: 14px; font-weight: 700; - margin-bottom: 5px; + margin-bottom: .75rem; color: $primary-500; } diff --git a/src/optimizer-page/scan-results/messages.js b/src/optimizer-page/scan-results/messages.js index 7b388bfe9..3368fda57 100644 --- a/src/optimizer-page/scan-results/messages.js +++ b/src/optimizer-page/scan-results/messages.js @@ -41,6 +41,14 @@ const messages = defineMessages({ id: 'course-authoring.course-optimizer.lockedLinkStatus', defaultMessage: 'Status: Locked', }, + recommendedManualCheckText: { + id: 'course-authoring.course-optimizer.recommendedManualCheckText', + defaultMessage: 'Recommended Manual Check', + }, + recommendedManualCheckTooltip: { + id: 'course-authoring.course-optimizer.recommendedManualCheckTooltip', + defaultMessage: 'For websites returning 403, websites often show 403 because they don\'t want bots accessing their content', + }, }); export default messages; diff --git a/src/optimizer-page/types.ts b/src/optimizer-page/types.ts index 831c69281..d87451631 100644 --- a/src/optimizer-page/types.ts +++ b/src/optimizer-page/types.ts @@ -7,6 +7,7 @@ export interface Unit { url: string; brokenLinks: string[]; lockedLinks: string[]; + externalForbiddenLinks: string[]; }[]; }