Skip to content

Redesign: general improvements 16 #1573

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

Merged
merged 11 commits into from
Jul 15, 2025
Merged
28 changes: 15 additions & 13 deletions src/app/components/elements/layout/SidebarLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import { AssignmentDTO, ContentSummaryDTO, GameboardDTO, GameboardItem, IsaacCon
import { above, ACCOUNT_TAB, ACCOUNT_TABS, AUDIENCE_DISPLAY_FIELDS, below, BOARD_ORDER_NAMES, BoardCompletions, BoardCreators, BoardLimit, BoardSubjects, BoardViews, confirmThen, determineAudienceViews, EventStageMap,
EventStatusFilter, EventTypeFilter, filterAssignmentsByStatus, filterAudienceViewsByProperties, getDistinctAssignmentGroups, getDistinctAssignmentSetters, getHumanContext, getThemeFromContextAndTags, HUMAN_STAGES,
ifKeyIsEnter, isAda, isDefined, PHY_NAV_SUBJECTS, isTeacherOrAbove, QuizStatus, siteSpecific, TAG_ID, tags, STAGE, useDeviceSize, LearningStage, HUMAN_SUBJECTS, ArrayElement, isFullyDefinedContext, isSingleStageContext,
stageLabelMap, extractTeacherName, determineGameboardSubjects, PATHS, getQuestionPlaceholder, getFilteredStageOptions, isPhy, ISAAC_BOOKS, BookHiddenState, TAG_LEVEL, VALID_APPS_CONTEXTS, getSearchPlaceholder} from "../../../services";
stageLabelMap, extractTeacherName, determineGameboardSubjects, PATHS, getQuestionPlaceholder, getFilteredStageOptions, isPhy, ISAAC_BOOKS, BookHiddenState, TAG_LEVEL, VALID_APPS_CONTEXTS, getSearchPlaceholder,
sortByStringValue} from "../../../services";
import { StageAndDifficultySummaryIcons } from "../StageAndDifficultySummaryIcons";
import { mainContentIdSlice, selectors, sidebarSlice, useAppDispatch, useAppSelector, useGetQuizAssignmentsAssignedToMeQuery } from "../../../state";
import { Link, useHistory, useLocation } from "react-router-dom";
Expand Down Expand Up @@ -168,8 +169,8 @@ interface RelatedContentSidebarProps extends SidebarProps {
}

const RelatedContentSidebar = (props: RelatedContentSidebarProps & {pageType: "concept" | "question" | "page"}) => {
const relatedConcepts = props.relatedContent?.filter(c => c.type === "isaacConceptPage") as IsaacConceptPageDTO[] | undefined;
const relatedQuestions = props.relatedContent?.filter(c => c.type === "isaacQuestionPage") as QuestionDTO[] | undefined;
const relatedConcepts = props.relatedContent?.filter(c => c.type === "isaacConceptPage").sort(sortByStringValue("title")) as IsaacConceptPageDTO[] | undefined;
const relatedQuestions = props.relatedContent?.filter(c => c.type === "isaacQuestionPage").sort(sortByStringValue("title")) as QuestionDTO[] | undefined;

const pageContext = useAppSelector(selectors.pageContext.context);
const pageContextStage = useAppSelector(selectors.pageContext.stage);
Expand Down Expand Up @@ -586,16 +587,9 @@ interface PracticeQuizzesSidebarProps extends ContentSidebarProps {
export const PracticeQuizzesSidebar = (props: PracticeQuizzesSidebarProps) => {
const { filterText, setFilterText, filterTags, setFilterTags, tagCounts, filterStages, setFilterStages, stageCounts, ...rest } = props;
const pageContext = useAppSelector(selectors.pageContext.context);
const subjectTag = tags.getById(pageContext?.subject as TAG_ID);
const fields = pageContext?.subject ? tags.getDirectDescendents(pageContext.subject as TAG_ID) : [];

const updateFilterTags = (tag: Tag) => {
if (filterTags?.includes(tag)) {
setFilterTags(filterTags.filter(t => t !== tag));
}
else {
setFilterTags([...(filterTags ?? []), tag]);
}
};

const updateFilterStages = (stage: Stage) => {
if (filterStages?.includes(stage)) {
Expand Down Expand Up @@ -651,10 +645,18 @@ export const PracticeQuizzesSidebar = (props: PracticeQuizzesSidebarProps) => {
<div className="section-divider"/>
<h5>Filter by topic</h5>
<ul className="ps-2">
<li>
<AllFiltersCheckbox
conceptFilters={filterTags ?? []} setConceptFilters={setFilterTags} tagCounts={tagCounts} baseTag={subjectTag}
/>
</li>
<div className="section-divider-small"/>
{fields.filter(tag => tagCounts[tag.id] > 0)
.map((tag, j) => <li key={j} >
<StyledTabPicker checkboxTitle={tag.title} checked={filterTags?.includes(tag)}
count={tagCounts[tag.id]} onInputChange={() => updateFilterTags(tag)}/>
<FilterCheckbox
tag={tag} conceptFilters={filterTags ?? []} setConceptFilters={setFilterTags}
tagCounts={tagCounts} incompatibleTags={[subjectTag]} baseTag={subjectTag}
/>
</li>)}
</ul>
</>}
Expand Down
36 changes: 21 additions & 15 deletions src/app/components/navigation/SupersededDeprecatedWarningBanner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export function SupersededDeprecatedWarningBanner({doc}: {doc: SeguePageDTO}) {
// Tutors and teachers should see superseded/deprecated messages because they have to setting assignments etc. and
// want up to date content.
const teacherMessage = isTutorOrAbove(user) && <React.Fragment>
<i id="superseded-help" className={siteSpecific("icon icon-info icon-color-grey", "icon-help")} />
<i id="superseded-help" className={siteSpecific("icon icon-info icon-color-grey ms-1", "icon-help")} />
<UncontrolledTooltip placement="bottom" target="superseded-help">
<div className="text-start">
{supersededBy && <>
Expand All @@ -47,20 +47,26 @@ export function SupersededDeprecatedWarningBanner({doc}: {doc: SeguePageDTO}) {
{isTutorOrAbove(user) && <strong>
Teacher note: {" "}
</strong>}
{doc.deprecated ? <>
This {contentType} is no longer supported, and may contain errors. {" "}
{supersededBy && <>
It has been replaced by {" "} <Button role="link" color="link" className="align-baseline" onClick={() => dispatch(goToSupersededByQuestion(doc))}>
this question
</Button>.
</>} {teacherMessage}
</> :
{doc.deprecated ? <div className="d-flex align-items-center">
<span>
This {contentType} is no longer supported, and may contain errors. {" "}
{supersededBy && <>
It has been replaced by {" "} <Button role="link" color="link" className="align-baseline" onClick={() => dispatch(goToSupersededByQuestion(doc))}>
this question
</Button>.
</>}
</span>
{teacherMessage}
</div> :
// If question is superseded but not deprecated
(supersededBy && !isStudent(user) ? <>
This question has been replaced by {" "}
<Button role="link" color="link" className="align-baseline" onClick={() => dispatch(goToSupersededByQuestion(doc))}>
this question
</Button>. {teacherMessage}
</> : RenderNothing)} {/* If neither deprecated or superseded, render nothing (although this should happen at the top of the component anyway) */}
(supersededBy && !isStudent(user) ? <div className="d-flex align-items-center">
<span>
This question has been replaced by {" "}
<Button role="link" color="link" className="align-baseline" onClick={() => dispatch(goToSupersededByQuestion(doc))}>
this question
</Button>.
</span>
{teacherMessage}
</div> : RenderNothing)} {/* If neither deprecated or superseded, render nothing (although this should happen at the top of the component anyway) */}
</Alert>;
}
5 changes: 1 addition & 4 deletions src/app/components/pages/Concepts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {Link, RouteComponentProps, withRouter} from "react-router-dom";
import {selectors, useAppSelector} from "../../state";
import {Badge, Card, CardBody, CardHeader, Container} from "reactstrap";
import queryString from "query-string";
import {above, below, getFilteredStageOptions, isAda, isPhy, isRelevantToPageContext, matchesAllWordsInAnyOrder, pushConceptsToHistory, searchResultIsPublic, shortcuts, TAG_ID, tags, useDeviceSize} from "../../services";
import {getFilteredStageOptions, isAda, isPhy, isRelevantToPageContext, matchesAllWordsInAnyOrder, pushConceptsToHistory, searchResultIsPublic, shortcuts, TAG_ID, tags} from "../../services";
import {generateSubjectLandingPageCrumbFromContext, TitleAndBreadcrumb} from "../elements/TitleAndBreadcrumb";
import {ShortcutResponse, Tag} from "../../../IsaacAppTypes";
import {IsaacSpinner} from "../handlers/IsaacSpinner";
Expand All @@ -15,8 +15,6 @@ import { useListConceptsQuery } from "../../state/slices/api/conceptsApi";
import { ShowLoadingQuery } from "../handlers/ShowLoadingQuery";
import { ContentSummaryDTO, Stage } from "../../../IsaacApiTypes";
import { skipToken } from "@reduxjs/toolkit/query";
import { AffixButton } from "../elements/AffixButton";
import classNames from "classnames";
import { PageMetadata } from "../elements/PageMetadata";

const subjectToTagMap = {
Expand All @@ -31,7 +29,6 @@ export const Concepts = withRouter((props: RouteComponentProps) => {
const {location, history} = props;
const user = useAppSelector(selectors.user.orNull);
const pageContext = useUrlPageTheme();
const deviceSize = useDeviceSize();

const searchParsed = queryString.parse(location.search, {arrayFormat: "comma"});

Expand Down
1 change: 0 additions & 1 deletion src/app/components/pages/QuestionFinder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,6 @@ export const QuestionFinder = withRouter(({location}: RouteComponentProps) => {
const params = useQueryParams<FilterParams, false>(false);
const history = useHistory();
const pageContext = useUrlPageTheme();
const deviceSize = useDeviceSize();
const [isSolitaryStage, setIsSolitaryStage] = useState(false); // we can't calculate this until we have the page context
const [selections, setSelections] = useState<ChoiceTree[]>([]); // we can't populate this until we have the page context
const [searchTopics, setSearchTopics] = useState<string[]>(arrayFromPossibleCsv(params.topics));
Expand Down
8 changes: 6 additions & 2 deletions src/app/components/pages/subjectLandingPageComponents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,10 @@ const MentoringSchemeCard = (context: NonNullable<Required<PageContextState>>):
return ArbitraryPageLinkCard("Mentoring scheme", "Take your problem solving skills to the next level by joining the mentoring scheme.", [{tag: "Find out more", url: "/pages/isaac_mentor"}])(context);
};

const ExperimentsCard = (context: NonNullable<Required<PageContextState>>): ListViewCardProps => {
return ArbitraryPageLinkCard("Experiments", "Develop experimental skills with interesting experiments.", [{tag: "Explore experiments", url: "/books/step_into_phys/exp_falling"}])(context);
};

const MathsRevisionCard = (context: NonNullable<Required<PageContextState>>): ListViewCardProps => {
return ArbitraryPageLinkCard("Revision", "Revise with our tailored revision decks on core pure, further pure, and mechanics.", [{tag: "List of revision decks", url: "/pages/revision_maths_alevel"}])(context);
};
Expand All @@ -123,7 +127,7 @@ const MathsUniCard = (context: NonNullable<Required<PageContextState>>): ListVie

const subjectSpecificCardsMap: {[subject in keyof typeof PHY_NAV_SUBJECTS]: {[stage in typeof PHY_NAV_SUBJECTS[subject][number]]: (LandingPageCard | null)[]}} = {
"physics": {
"11_14": [StepUpPhyCard, AnvilAppsCoreCard, null],
"11_14": [QuestionFinderCard, ConceptPageCard, AnvilAppsCoreCard],
"gcse": [BoardsByTopicCard, LessonsAndRevisionCard, CoreSkillsCard],
"a_level": [BoardsByTopicCard, LessonsAndRevisionCard, MentoringSchemeCard],
"university": [BoardsByTopicCard, MathsUniCard, null],
Expand Down Expand Up @@ -177,7 +181,7 @@ export const getLandingPageCardsForContext = (context: PageContextState, stacked

const baseCards: LandingPageCard[] =
context.stage.includes("11_14") && context.subject === "physics"
? [StepIntoPhyCard, ConceptPageCard, QuestionFinderCard]
? [StepIntoPhyCard, StepUpPhyCard, ExperimentsCard]
: context.stage.includes("gcse") && (context.subject === "chemistry" || context.subject === "maths")
? [QuestionFinderCard, ConceptPageCard]
: [QuestionFinderCard, ConceptPageCard, PracticeTestsCard];
Expand Down
8 changes: 6 additions & 2 deletions src/scss/common/elements.scss
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@
}

.new-tab {
height: 24px;
width: 24px;
height: fit-content;
background: none;
> img {
height: 24px;
width: 24px;
}
}

.content-summary-link {
Expand Down
6 changes: 6 additions & 0 deletions src/scss/phy/sidebar.scss
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,12 @@
}
}

[type='search'] {
&.search--filter-input {
max-width: 100% !important;
}
}

#content-sidebar-offcanvas {
background-color: $color-neutral-50;

Expand Down