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[];
}[];
}