Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ada core and advanced tagging #1014

Merged
merged 13 commits into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/IsaacApiTypes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -559,9 +559,9 @@ export interface GroupMembershipDTO {
created?: Date;
}

export type Stage = "year_7_and_8" | "year_9" | "gcse" | "a_level" | "further_a" | "university" | "scotland_national_5" | "scotland_higher" | "scotland_advanced_higher" | "all";
export type Stage = "year_7_and_8" | "year_9" | "gcse" | "a_level" | "further_a" | "university" | "scotland_national_5" | "scotland_higher" | "scotland_advanced_higher" | "core" | "advanced" | "all";

export type ExamBoard = "aqa" | "cie" | "edexcel" | "eduqas" | "ocr" | "wjec" | "sqa" | "all";
export type ExamBoard = "aqa" | "cie" | "edexcel" | "eduqas" | "ocr" | "wjec" | "sqa" | "ada" | "all";

export type Difficulty = "practice_1" | "practice_2" | "practice_3" | "challenge_1" | "challenge_2" | "challenge_3";

Expand Down
13 changes: 7 additions & 6 deletions src/app/components/content/IsaacAccordion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export const IsaacAccordion = ({doc}: {doc: ContentDTO}) => {

// Handle conditional display settings
.map(section => {
let sectionDisplay = mergeDisplayOptions(accordionDisplay, section.display);
const sectionDisplay = mergeDisplayOptions(accordionDisplay, section.display);
const sectionDisplaySettings = isIntendedAudience(section.audience, userContext, user) ?
sectionDisplay?.["audience"] : sectionDisplay?.["nonAudience"];
if (sectionDisplaySettings?.includes("open")) {section.startOpen = true;}
Expand Down Expand Up @@ -87,15 +87,16 @@ export const IsaacAccordion = ({doc}: {doc: ContentDTO}) => {
// Filter out hidden sections before they mess up indexing
.filter(section => !section.hidden)

.map((section , index) =>
<Accordion
.map((section , index) => {
const intendedAudience = isIntendedAudience(section.audience, userContext, user);
return <Accordion
key={`${section.sectionIndex} ${index}`} id={section.id} index={index}
startOpen={section.startOpen} deEmphasised={section.deEmphasised}
trustedTitle={section?.title || ""}
audienceString={stringifyAudience(section.audience, userContext)}
audienceString={stringifyAudience(section.audience, userContext, intendedAudience)}
>
<IsaacContent doc={section} />
</Accordion>
)}
</Accordion>;
})}
</div>;
};
10 changes: 4 additions & 6 deletions src/app/components/elements/Accordion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
above,
ALPHABET,
audienceStyle,
below,
DOCUMENT_TYPE,
isAda,
isAQuestionLikeDoc,
Expand All @@ -14,7 +13,6 @@ import {
scrollVerticallyIntoView,
siteSpecific,
useDeviceSize,
useUserViewingContext
} from "../../services";
import {AppState, logAction, selectors, useAppDispatch, useAppSelector} from "../../state";
import {AccordionSectionContext} from "../../../IsaacAppTypes";
Expand Down Expand Up @@ -176,10 +174,10 @@ export const Accordion = withRouter(({id, trustedTitle, index, children, startOp
{isConceptPage && audienceString && <span className={"stage-label text-bg-secondary d-flex align-items-center p-2 " +
"justify-content-center " + classNames({[audienceStyle(audienceString)]: isAda})}>
{siteSpecific(
audienceString,
(above["sm"](deviceSize) ? audienceString : audienceString.replaceAll(",", "\n")).split("\n").map((line, i, arr) => <>
audienceString,
above["sm"](deviceSize) ? audienceString : audienceString.replaceAll(",", "\n")).split("\n").map((line, i, arr) => <>
{line}{i < arr.length && <br/>}
</>)
</>
)}
</span>}
<div className="accordion-title ps-3">
Expand Down Expand Up @@ -221,4 +219,4 @@ export const Accordion = withRouter(({id, trustedTitle, index, children, startOp
</AccordionSectionContext.Provider>
</RS.Collapse>
</div>;
});
});
36 changes: 24 additions & 12 deletions src/app/components/elements/inputs/UserContextAccountInput.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, {useRef} from "react";
import React, {ChangeEvent, useEffect, useRef} from "react";
import {BooleanNotation, DisplaySettings, ValidationUser} from "../../../../IsaacAppTypes";
import {
EMPTY_BOOLEAN_NOTATION_RECORD,
Expand All @@ -20,6 +20,7 @@ import {v4 as uuid_v4} from "uuid";
import classNames from "classnames";
import {Immutable} from "immer";
import {StyledDropdown} from "./DropdownInput";
import { isUndefined } from "lodash";
import { StyledCheckbox } from "./StyledCheckbox";

interface UserContextRowProps {
Expand All @@ -39,11 +40,11 @@ interface UserContextRowProps {

function UserContextRow({
userContext, setUserContext, showNullStageOption, submissionAttempted, existingUserContexts, setBooleanNotation, setDisplaySettings,
tutorOrAbove, userContexts, setUserContexts, index, required
tutorOrAbove, userContexts, setUserContexts, index, required: _required
}: UserContextRowProps) {
const onlyUCWithThisStage = existingUserContexts.length === 0 || existingUserContexts.filter(uc => uc.stage === userContext.stage).length === 1;

const onStageUpdate = (e: any) => {
const onStageUpdate = (e: ChangeEvent<HTMLInputElement>) => {
const stage = e.target.value as STAGE;
if (!isAda) {
setUserContext({...userContext, stage});
Expand All @@ -53,22 +54,34 @@ function UserContextRow({
const onlyOneAtThisStage = existingUserContexts.length === 0 || existingUserContexts.filter(uc => uc.stage === e.target.value).length === 1;
const possibleExamBoards = getFilteredExamBoardOptions(
{byStages: [stage || STAGE.ALL], byUserContexts: existingUserContexts, includeNullOptions: onlyOneAtThisStage
}) || [EXAM_BOARD.ALL];
const examBoard = possibleExamBoards.map(e => e.value).includes(userContext.examBoard as EXAM_BOARD) && userContext.examBoard || possibleExamBoards[0].value;
});
const examBoards = possibleExamBoards.map(e => e.value) || [EXAM_BOARD.ADA];
const examBoard = examBoards.includes(userContext.examBoard as EXAM_BOARD) && userContext.examBoard || possibleExamBoards[0].value;
setBooleanNotation({...EMPTY_BOOLEAN_NOTATION_RECORD, [examBoardBooleanNotationMap[examBoard]]: true});

// Set display settings default values
setDisplaySettings(oldDs => ({...oldDs, HIDE_NON_AUDIENCE_CONTENT: true}));
setUserContext({...userContext, stage, examBoard});
};

const onExamBoardUpdate = (e: any) => {
setUserContext({...userContext, examBoard: e.target.value as EXAM_BOARD});
const onExamBoardUpdate = (e: ChangeEvent<HTMLInputElement>) => {
const examBoard: EXAM_BOARD = e.target.value as EXAM_BOARD;
setUserContext({...userContext, examBoard: examBoard});
if (e.target.value) {
setBooleanNotation({...EMPTY_BOOLEAN_NOTATION_RECORD, [examBoardBooleanNotationMap[e.target.value as EXAM_BOARD]]: true});
setBooleanNotation({...EMPTY_BOOLEAN_NOTATION_RECORD, [examBoardBooleanNotationMap[examBoard]]: true});
}
};

useEffect(() => {
// Deliberately set default user context on Ada for users who have no context
if (isAda && (isUndefined(userContext.stage) || isUndefined(userContext.examBoard))) {
const stage = userContext.stage ?? STAGE.ALL;
const examBoard = userContext.examBoard ?? EXAM_BOARD.ADA;
setUserContext({stage, examBoard});
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [setUserContext]);

return <React.Fragment>
{/* Stage Selector */}
<div className="d-flex flex-row justify-content-between">
Expand All @@ -79,7 +92,7 @@ function UserContextRow({
onChange={onStageUpdate}
value={userContext.stage}
>
<option value=""/>
{isPhy && <option value="" className="d-none"/>}
{getFilteredStageOptions({
byUserContexts: existingUserContexts.filter(uc => !(uc.stage === userContext.stage && uc.examBoard === userContext.examBoard)),
includeNullOptions: showNullStageOption, hideFurtherA: true
Expand All @@ -96,7 +109,6 @@ function UserContextRow({
onChange={onExamBoardUpdate}
value={userContext.examBoard}
>
<option value="" />
{getFilteredExamBoardOptions({
byStages: [userContext.stage as STAGE || STAGE.ALL],
includeNullOptions: onlyUCWithThisStage,
Expand All @@ -110,7 +122,7 @@ function UserContextRow({
{tutorOrAbove && userContexts.length > 1 && <Button close
className="close float-none bg-white p-2" aria-label="clear stage row"
disabled={userContexts.length <= 1}
onClick={() => setUserContexts(userContexts.filter((uc, i) => i !== index))}
onClick={() => setUserContexts(userContexts.filter((_uc, i) => i !== index))}
/>}
</div>
</div>
Expand Down Expand Up @@ -217,4 +229,4 @@ export function UserContextAccountInput({
</div>}
</div>
</div>;
}
}
5 changes: 3 additions & 2 deletions src/app/components/elements/inputs/UserContextPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,9 @@ export const UserContextPicker = ({className, hideLabels = true}: {className?: s
const newParams: {[key: string]: unknown} = {...qParams, stage: e.target.value};
const stage = e.target.value as STAGE;
if (isAda) {
// Drive exam board selection so that it is a valid option - by default use All.
let examBoard = EXAM_BOARD.ALL;
// Derive exam board selection so that it is a valid option
// Try to use default preferences (Stage All, Ada) otherwise default to All.
let examBoard = stage === STAGE.ALL ? EXAM_BOARD.ADA : EXAM_BOARD.ALL;
const possibleExamBoards =
getFilteredExamBoardOptions({byUser: user, byStages: [stage], includeNullOptions: true})
.map(eb => eb.value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ export const ContentSummaryListGroupItem = ({item, search, displayTopicTitle}: {
</div>}
</div>

{!isContentsIntendedAudience && <div className="ms-auto me-3 d-flex align-items-center">
{isPhy && !isContentsIntendedAudience && <div className="ms-auto me-3 d-flex align-items-center">
<span id={`audience-help-${componentId}`} className="icon-help mx-1" />
<UncontrolledTooltip placement="bottom" target={`audience-help-${componentId}`}>
{`This content has ${notRelevantMessage(userContext)}.`}
Expand Down
17 changes: 11 additions & 6 deletions src/app/components/elements/list-groups/TopicSummaryLinks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
DOCUMENT_TYPE,
documentTypePathPrefix,
isAda,
isPhy,
isIntendedAudience,
makeIntendedAudienceComparator,
notRelevantMessage,
Expand Down Expand Up @@ -45,18 +46,22 @@ export function TopicSummaryLinks({items, search}: {items: ContentSummaryDTO[];

// Render remaining items
.map((item, index) => {
const audienceString = stringifyAudience(item.audience, userContext);
const audienceString = stringifyAudience(
item.audience, userContext,
isIntendedAudience(item.audience, userContext, user)
);

return <RS.ListGroupItem key={item.id} className="topic-summary-link">
<RS.Button
tag={Link} to={{pathname: `/${documentTypePathPrefix[DOCUMENT_TYPE.CONCEPT]}/${item.id}`, search}}
block color="link" className={"d-flex align-items-stretch " + classNames({"de-emphasised": item.deEmphasised})}
>
<div className={"stage-label text-bg-primary d-flex align-items-center justify-content-center " + classNames({[audienceStyle(audienceString)]: isAda})}>
{siteSpecific(
audienceString,
(above["sm"](deviceSize) ? audienceString : audienceString.replaceAll(",", "\n")).split("\n").map((line, i, arr) => <>
{line}{i < arr.length && <br/>}
</>)
audienceString,
above["sm"](deviceSize) ? audienceString : audienceString.replaceAll(",", "\n")).split("\n").map((line, i, arr) => <>
{line}{i < arr.length && <br/>}
</>
)}
</div>
<div className="title ps-3 d-flex">
Expand All @@ -65,7 +70,7 @@ export function TopicSummaryLinks({items, search}: {items: ContentSummaryDTO[];
{item.title}
</Markup>
</div>
{item.deEmphasised && <div className="ms-auto me-3 d-flex align-items-center">
{isPhy && item.deEmphasised && <div className="ms-auto me-3 d-flex align-items-center">
<span id={`audience-help-${index}`} className="icon-help mx-1" />
<RS.UncontrolledTooltip placement="bottom" target={`audience-help-${index}`}>
{`This content has ${notRelevantMessage(userContext)}.`}
Expand Down
2 changes: 1 addition & 1 deletion src/app/components/elements/modals/LoginOrSignUpModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,6 @@ const LoginOrSignUpBody = () => {
export const loginOrSignUpModal = {
centered: true,
noPadding: true,
closeAction: () => {store.dispatch(closeActiveModal())},
closeAction: () => {store.dispatch(closeActiveModal());},
body: LoginOrSignUpBody
};
4 changes: 3 additions & 1 deletion src/app/components/pages/ExamSpecifications.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ export const ExamSpecifications = () => {
[STAGE.SCOTLAND_NATIONAL_5]: "Discover our free National 5 computer science topics and questions. Learn or revise for your exams with us today.",
[STAGE.SCOTLAND_HIGHER]: "Discover our free Higher computer science topics and questions. Learn or revise for your exams with us today.",
[STAGE.SCOTLAND_ADVANCED_HIGHER]: "Discover our free Advanced Higher computer science topics and questions. Learn or revise for your exams with us today.",
[STAGE.CORE]: "Discover our free Core computer science topics and questions. Learn or revise for your exams with us today.",
[STAGE.ADVANCED]: "Discover our free Advanced computer science topics and questions. Learn or revise for your exams with us today.",
})[stageTab];

const examBoardTabs = STAGES_WITH_EXAM_SPECIFICATIONS.reduce((acc: {[stage: string]: React.JSX.Element}, stage) => ({
Expand Down Expand Up @@ -81,4 +83,4 @@ export const ExamSpecifications = () => {
</Col>
</Row>
</Container>;
};
};
8 changes: 4 additions & 4 deletions src/app/components/pages/LogIn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export const useLoginLogic = () => {
setStateFunctions: {setEmail, setPassword, setRememberMe, setPasswordResetAttempted},
loginValues: {email, totpChallengePending, errorMessage, logInAttempted, passwordResetAttempted, rememberMe, isValidEmail, isValidPassword}
};
}
};

// Handles display and logic of the two-factor authentication form (usually shown after the first login step)
export const TFAInput = React.forwardRef(function TFAForm({rememberMe}: {rememberMe: boolean}, ref: React.Ref<HTMLHeadingElement>) {
Expand Down Expand Up @@ -106,7 +106,7 @@ export const TFAInput = React.forwardRef(function TFAForm({rememberMe}: {remembe
onClick={(event) => {
event.preventDefault();
if (mfaVerificationCode)
dispatch(submitTotpChallengeResponse(mfaVerificationCode, rememberMe))
dispatch(submitTotpChallengeResponse(mfaVerificationCode, rememberMe));
}}
/>
</FormGroup>
Expand Down Expand Up @@ -141,7 +141,7 @@ export const PasswordResetButton = ({email, isValidEmail, setPasswordResetAttemp
Please check your inbox.
</strong>}
</p>;
}
};

interface EmailPasswordInputsProps {
setEmail: (email: string) => void;
Expand Down Expand Up @@ -183,7 +183,7 @@ export const EmailPasswordInputs =({setEmail, setPassword, validEmail, validPass
</FormFeedback>
</FormGroup>
</>;
}
};

// Main login page component, utilises all of the components defined above
export const LogIn = () => {
Expand Down
10 changes: 5 additions & 5 deletions src/app/components/pages/RegistrationAgeCheck.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export const RegistrationAgeCheck = () => {
over13 ?
history.push("/register/student/details")
:
history.push("/register/student/age_denied")
history.push("/register/student/age_denied");
}
};

Expand All @@ -45,7 +45,7 @@ export const RegistrationAgeCheck = () => {
className="d-inline"
type="radio"
checked={over13 === true}
onChange={() => {setOver13(true)}}
onChange={() => {setOver13(true);}}
color="secondary"
invalid={submissionAttempted && over13 === undefined}
/>
Expand All @@ -57,7 +57,7 @@ export const RegistrationAgeCheck = () => {
className="d-inline"
type="radio"
checked={over13 === false}
onChange={() => {setOver13(false)}}
onChange={() => {setOver13(false);}}
color="secondary"
invalid={submissionAttempted && over13 === undefined}
>
Expand All @@ -79,5 +79,5 @@ export const RegistrationAgeCheck = () => {
</Form>
</CardBody>
</Card>
</Container>
}
</Container>;
};
8 changes: 4 additions & 4 deletions src/app/components/pages/RegistrationRoleSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ export const RegistrationRoleSelect = () => {

const studentSignup = (event: React.MouseEvent) => {
event.preventDefault();
history.push("/register/student/age")
}
history.push("/register/student/age");
};

return <Container>
<TitleAndBreadcrumb currentPageTitle={`Create an ${SITE_TITLE} account`} className="mb-4" />
Expand Down Expand Up @@ -65,5 +65,5 @@ export const RegistrationRoleSelect = () => {
</Row>
</CardBody>
</Card>
</Container>
}
</Container>;
};
Loading