Skip to content

Commit efa4c53

Browse files
committed
Merge branch 'redesign-2024' of https://github.com/isaacphysics/isaac-react-app into redesign/component/teacher-dashboard
2 parents 34ac73a + 82ee33f commit efa4c53

10 files changed

+149
-53
lines changed

src/app/components/elements/cards/GameboardCard.tsx

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ export const GameboardCard = (props: GameboardCardProps) => {
4949

5050
const card = <div className="px-3 py-2 flex-grow-1">
5151
<Row data-testid="my-assignment">
52-
<Col md={8} className="d-flex flex-column align-items-start">
52+
<Col sm={isSetAssignments ? 8 : 12} md={8} className="d-flex flex-column align-items-start">
5353
<div className="d-flex align-items-center">
5454
<div className="d-flex justify-content-center board-subject-hexagon-size me-4 my-2">
5555
<div className="board-subject-hexagon-container justify-content-center">
@@ -75,13 +75,13 @@ export const GameboardCard = (props: GameboardCardProps) => {
7575

7676
<Spacer/>
7777

78-
{above['md'](deviceSize) && <Button className="my-2 btn-underline" color="link" onClick={(e) => {e.preventDefault(); setShowMore(!showMore);}}>
78+
{above[isSetAssignments ? 'sm' : 'md'](deviceSize) && <Button className="my-2 btn-underline" color="link" onClick={(e) => {e.preventDefault(); setShowMore(!showMore);}}>
7979
{showMore ? "Hide details" : "Show details"}
8080
</Button>}
8181
</Col>
8282

83-
<Col md={4} className="d-flex flex-column justify-content-between">
84-
<div className={classNames("d-flex flex-wrap justify-content-center justify-content-md-end",
83+
<Col sm={isSetAssignments ? 4 : 12} md={4} className="d-flex flex-column justify-content-between">
84+
<div className={classNames("d-flex flex-wrap justify-content-center justify-content-sm-end",
8585
{"justify-content-lg-center justify-content-xl-end column-gap-5 column-gap-md-4": !isSetAssignments},
8686
)}>
8787
{!isSetAssignments
@@ -111,15 +111,21 @@ export const GameboardCard = (props: GameboardCardProps) => {
111111
}
112112
</div>
113113
{isSetAssignments
114-
? <Button color="keyline" onClick={(e) => {e.preventDefault(); setAssignmentsDetails.toggleAssignModal?.();}}>
114+
? above['md'](deviceSize) && <Button className="mb-2" color="keyline" onClick={(e) => {e.preventDefault(); setAssignmentsDetails.toggleAssignModal?.();}}>
115115
Assign{!isDefined(setAssignmentsDetails.groupCount) || setAssignmentsDetails.groupCount > 0 && " / Unassign"}
116116
</Button>
117117
: boardLink && <div className="card-share-link">
118118
<ShareLink linkUrl={boardLink} gameboardId={gameboard.id} reducedWidthLink clickAwayClose />
119119
</div>
120120
}
121121

122-
{!above['md'](deviceSize) && <Button className="my-2 btn-underline w-max-content" color="link" onClick={(e) => {e.preventDefault(); setShowMore(!showMore);}}>
122+
{isSetAssignments && !above['md'](deviceSize) &&
123+
<Button className="mb-2" color="keyline" onClick={(e) => {e.preventDefault(); setAssignmentsDetails.toggleAssignModal?.();}}>
124+
Assign{!isDefined(setAssignmentsDetails.groupCount) || setAssignmentsDetails.groupCount > 0 && " / Unassign"}
125+
</Button>
126+
}
127+
128+
{!above[isSetAssignments ? 'sm' : 'md'](deviceSize) && <Button className="my-2 btn-underline w-max-content" color="link" onClick={(e) => {e.preventDefault(); setShowMore(!showMore);}}>
123129
{showMore ? "Hide details" : "Show details"}
124130
</Button>}
125131
</Col>

src/app/components/elements/layout/SidebarLayout.tsx

Lines changed: 72 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,16 @@ import { Col, ColProps, RowProps, Input, Label, Offcanvas, OffcanvasBody, Offcan
33
import partition from "lodash/partition";
44
import classNames from "classnames";
55
import { AssignmentDTO, ContentSummaryDTO, IsaacConceptPageDTO, QuestionDTO } from "../../../../IsaacApiTypes";
6-
import { above, AUDIENCE_DISPLAY_FIELDS, BoardCompletions, BoardCreators, BoardLimit, BoardViews, determineAudienceViews, filterAssignmentsByStatus, filterAudienceViewsByProperties, getDistinctAssignmentGroups, getDistinctAssignmentSetters, getThemeFromContextAndTags, isAda, isDefined, siteSpecific, stageLabelMap, useDeviceSize } from "../../../services";
6+
import { above, AUDIENCE_DISPLAY_FIELDS, BOARD_ORDER_NAMES, BoardCompletions, BoardCreators, BoardLimit, BoardSubjects, BoardViews, determineAudienceViews, filterAssignmentsByStatus, filterAudienceViewsByProperties, getDistinctAssignmentGroups, getDistinctAssignmentSetters, getThemeFromContextAndTags, isAda, isDefined, siteSpecific, stageLabelMap, useDeviceSize } from "../../../services";
77
import { StageAndDifficultySummaryIcons } from "../StageAndDifficultySummaryIcons";
88
import { selectors, useAppSelector } from "../../../state";
99
import { Link } from "react-router-dom";
10-
import { Tag } from "../../../../IsaacAppTypes";
10+
import { AssignmentBoardOrder, Tag } from "../../../../IsaacAppTypes";
1111
import { AffixButton } from "../AffixButton";
1212
import { getHumanContext } from "../../../services/pageContext";
1313
import { AssignmentState } from "../../pages/MyAssignments";
1414
import { ShowLoadingQuery } from "../../handlers/ShowLoadingQuery";
15+
import { Spacer } from "../Spacer";
1516

1617
export const SidebarLayout = (props: RowProps) => {
1718
const { className, ...rest } = props;
@@ -418,14 +419,17 @@ export const MyGameboardsSidebar = (props: MyGameboardsSidebarProps) => {
418419
onChange={(e: ChangeEvent<HTMLInputElement>) => setBoardTitleFilter(e.target.value)}
419420
/>
420421
<div className="section-divider"/>
421-
<h5 className="mb-4">Display mode</h5>
422-
<Input type="select" value={displayMode} onChange={e => setDisplayMode(e.target.value as BoardViews)}>
423-
{Object.values(BoardViews).map(view => <option key={view} value={view}>{view}</option>)}
424-
</Input>
425-
<h5 className="mt-4 mb-3">Display limit</h5>
426-
<Input type="select" value={displayLimit} onChange={e => setDisplayLimit(e.target.value as BoardLimit)}>
427-
{Object.values(BoardLimit).map(limit => <option key={limit} value={limit}>{limit}</option>)}
428-
</Input>
422+
<h5 className="mb-4">Display</h5>
423+
<div className="d-flex">
424+
<Input className="w-auto" type="select" value={displayMode} onChange={e => setDisplayMode(e.target.value as BoardViews)}>
425+
{Object.values(BoardViews).map(view => <option key={view} value={view}>{view}</option>)}
426+
</Input>
427+
<Spacer/>
428+
<div className="select-pretext">Limit:</div>
429+
<Input className="w-auto" type="select" value={displayLimit} onChange={e => setDisplayLimit(e.target.value as BoardLimit)}>
430+
{Object.values(BoardLimit).map(limit => <option key={limit} value={limit}>{limit}</option>)}
431+
</Input>
432+
</div>
429433
<h5 className="mt-4 mb-3">Filter by creator</h5>
430434
<Input type="select" value={boardCreatorFilter} onChange={e => setBoardCreatorFilter(e.target.value as BoardCreators)}>
431435
{Object.values(BoardCreators).map(creator => <option key={creator} value={creator}>{creator}</option>)}
@@ -436,10 +440,65 @@ export const MyGameboardsSidebar = (props: MyGameboardsSidebarProps) => {
436440
</Input>
437441
</ContentSidebar>;
438442
};
443+
interface SetAssignmentsSidebarProps extends SidebarProps {
444+
displayMode: BoardViews;
445+
setDisplayMode: React.Dispatch<React.SetStateAction<BoardViews>>;
446+
displayLimit: BoardLimit;
447+
setDisplayLimit: React.Dispatch<React.SetStateAction<BoardLimit>>;
448+
boardTitleFilter: string;
449+
setBoardTitleFilter: React.Dispatch<React.SetStateAction<string>>;
450+
sortOrder: AssignmentBoardOrder;
451+
setSortOrder: React.Dispatch<React.SetStateAction<AssignmentBoardOrder>>;
452+
boardSubject: BoardSubjects;
453+
setBoardSubject: React.Dispatch<React.SetStateAction<BoardSubjects>>;
454+
boardCreator: BoardCreators;
455+
setBoardCreator: React.Dispatch<React.SetStateAction<BoardCreators>>;
456+
sortDisabled?: boolean;
457+
}
439458

440-
export const SetAssignmentsSidebar = (props: SidebarProps) => {
441-
// TODO
442-
return <ContentSidebar {...props}/>;
459+
export const SetAssignmentsSidebar = (props: SetAssignmentsSidebarProps) => {
460+
const { displayMode, setDisplayMode, displayLimit, setDisplayLimit, boardTitleFilter, setBoardTitleFilter, sortOrder, setSortOrder, sortDisabled, boardSubject, setBoardSubject, boardCreator, setBoardCreator, ...rest } = props;
461+
462+
return <ContentSidebar {...rest} className={classNames(rest.className, "pt-0")}>
463+
<div className="section-divider"/>
464+
<h5>Search gameboards</h5>
465+
<Input
466+
className='search--filter-input my-4'
467+
type="search" value={boardTitleFilter || ""}
468+
placeholder="e.g. Forces"
469+
onChange={(e: ChangeEvent<HTMLInputElement>) => setBoardTitleFilter(e.target.value)}
470+
/>
471+
<div className="section-divider"/>
472+
<h5 className="mb-4">Display</h5>
473+
<div className="d-flex">
474+
<Input className="w-auto" type="select" value={displayMode} onChange={e => setDisplayMode(e.target.value as BoardViews)}>
475+
{Object.values(BoardViews).map(view => <option key={view} value={view}>{view}</option>)}
476+
</Input>
477+
<Spacer/>
478+
<div className="select-pretext">Limit:</div>
479+
<Input className="w-auto" type="select" value={displayLimit} onChange={e => setDisplayLimit(e.target.value as BoardLimit)}>
480+
{Object.values(BoardLimit).map(limit => <option key={limit} value={limit}>{limit}</option>)}
481+
</Input>
482+
</div>
483+
<h5 className="mt-4 mb-3">Sort by</h5>
484+
<Input type="select" className="mb-3" value={sortOrder} onChange={e => setSortOrder(e.target.value as AssignmentBoardOrder)} disabled={sortDisabled}>
485+
{Object.values(AssignmentBoardOrder).filter(
486+
order => !['attempted', '-attempted', 'correct', '-correct'].includes(order)
487+
).map(order => <option key={order} value={order}>{BOARD_ORDER_NAMES[order]}</option>)}
488+
</Input>
489+
{sortDisabled && <div className="small text-muted mt-2">
490+
Sorting is disabled if some gameboards are hidden. Increase the display limit to show all gameboards.
491+
</div>}
492+
<div className="section-divider"/>
493+
<h5 className="mb-3">Filter by subject</h5>
494+
<Input type="select" value={boardSubject} onChange={e => setBoardSubject(e.target.value as BoardSubjects)}>
495+
{Object.values(BoardSubjects).map(subject => <option key={subject} value={subject}>{subject}</option>)}
496+
</Input>
497+
<h5 className="mt-4 mb-3">Filter by creator</h5>
498+
<Input type="select" value={boardCreator} onChange={e => setBoardCreator(e.target.value as BoardCreators)}>
499+
{Object.values(BoardCreators).map(creator => <option key={creator} value={creator}>{creator}</option>)}
500+
</Input>
501+
</ContentSidebar>;
443502
};
444503

445504
export const MyAccountSidebar = (props: SidebarProps) => {

src/app/components/pages/MyGameboards.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,8 +199,10 @@ export const MyGameboards = () => {
199199
:
200200
<>
201201
<div className="mt-4 mb-2">
202-
{boards && <h4>Showing <strong>{inProgress + notStarted}</strong> {siteSpecific("gameboards", "quizzes")}, with <strong>{inProgress}</strong> on the go and <strong>{notStarted}</strong> not started</h4>}
203-
{!boards && <IsaacSpinner size="sm" inline />}
202+
{boards
203+
? <h4>Showing <strong>{inProgress + notStarted}</strong> {siteSpecific("gameboards", "quizzes")}, with <strong>{inProgress}</strong> on the go and <strong>{notStarted}</strong> not started</h4>
204+
: <IsaacSpinner size="sm" inline />
205+
}
204206
</div>
205207
{isAda && <>
206208
{/* this is in the sidebar on phy */}

src/app/components/pages/SetAssignments.tsx

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ const AssignGroup = ({groups, board}: AssignGroupProps) => {
111111
}
112112
}
113113

114-
return <Container className="py-2">
114+
return <Container fluid className="py-2">
115115
<Label className="w-100 pb-2">Group(s):
116116
<StyledSelect inputId="groups-to-assign" isMulti isClearable placeholder="None"
117117
value={selectedGroups}
@@ -531,7 +531,15 @@ export const SetAssignments = () => {
531531
<TitleAndBreadcrumb currentPageTitle={siteSpecific("Set assignments", "Manage assignments")} help={pageHelp} modalId="help_modal_set_assignments"/>
532532
<PageFragment fragmentId={siteSpecific("help_toptext_set_gameboards", "set_quizzes_help")} ifNotFound={RenderNothing} />
533533
<SidebarLayout>
534-
<SetAssignmentsSidebar/>
534+
<SetAssignmentsSidebar
535+
displayMode={boardView} setDisplayMode={setBoardView}
536+
displayLimit={boardLimit} setDisplayLimit={setBoardLimit}
537+
boardTitleFilter={boardTitleFilter} setBoardTitleFilter={setBoardTitleFilter}
538+
sortOrder={boardOrder} setSortOrder={setBoardOrder}
539+
boardSubject={boardSubject} setBoardSubject={setBoardSubject}
540+
boardCreator={boardCreator} setBoardCreator={setBoardCreator}
541+
sortDisabled={!!boards && boards.boards.length !== boards.totalResults}
542+
/>
535543
<MainContent>
536544
{isPhy && <PhyAddGameboardButtons className={"mb-4"} redirectBackTo={PATHS.SET_ASSIGNMENTS}/>}
537545
{groups && groups.length === 0 && <Alert color="warning">
@@ -549,9 +557,10 @@ export const SetAssignments = () => {
549557
)}
550558
</h3>
551559
: <>
552-
{isPhy && <h4>
560+
{isPhy && <h5>
553561
Use the <Link to={"/assignment_schedule"}>assignment schedule</Link> page to view assignments by start date and due date.
554-
</h4>}
562+
<div className="section-divider my-4"/>
563+
</h5>}
555564
{isAda && <>
556565
{boards && boards.totalResults > 0 && <h4>
557566
You have <strong>{boards.totalResults}</strong> quiz{boards.totalResults > 1 && "zes"} ready to assign...{" "}
@@ -563,8 +572,8 @@ export const SetAssignments = () => {
563572
You have <IsaacSpinner size="sm" inline/> {siteSpecific("gameboards", "quizzes")} ready to assign...
564573
</h4>}
565574
</>}
566-
<Row>
567-
{(isPhy || boardView === BoardViews.card) && <Col sm={6} lg={3}>
575+
{isAda && <Row>
576+
{boardView === BoardViews.card && <Col sm={6} lg={3}>
568577
<Label className="w-100">
569578
Display in <Input type="select" value={boardView} onChange={switchView}>
570579
{Object.values(BoardViews).map(view => <option key={view} value={view}>{view}</option>)}
@@ -587,23 +596,29 @@ export const SetAssignments = () => {
587596
</Label>
588597
</Col>
589598
</>}
590-
</Row>
599+
</Row>}
591600
<ShowLoading until={boards}>
592601
{boards && boards.boards && <div>
593602
{boardView == BoardViews.card ?
594603
// Card view
595604
<>
596605
<Row className={siteSpecific("row-cols-1", "row-cols-lg-3 row-cols-md-2 row-cols-1")}>
597-
{boards.boards && boards.boards.map(board =>
598-
<Col key={board.id}>
599-
<BoardCard
600-
user={user}
601-
board={board}
602-
boardView={boardView}
603-
assignees={(isDefined(board?.id) && groupsByGameboard[board.id]) || []}
604-
toggleAssignModal={() => openAssignModal(board)}
605-
/>
606-
</Col>)}
606+
{boards.boards && boards.boards
607+
.filter(board => matchesAllWordsInAnyOrder(board.title, boardTitleFilter))
608+
.filter(board => formatBoardOwner(user, board) == boardCreator || boardCreator == "All")
609+
.filter(board => boardSubject == "All" || (determineGameboardSubjects(board).includes(boardSubject.toLowerCase())))
610+
.map(board =>
611+
<Col key={board.id}>
612+
<BoardCard
613+
user={user}
614+
board={board}
615+
boardView={boardView}
616+
assignees={(isDefined(board?.id) && groupsByGameboard[board.id]) || []}
617+
toggleAssignModal={() => openAssignModal(board)}
618+
/>
619+
</Col>
620+
)
621+
}
607622
</Row>
608623
<div className="text-center mt-3 mb-4" style={{clear: "both"}}>
609624
<p>Showing <strong>{boards.boards.length}</strong> of <strong>{boards.totalResults}</strong>

0 commit comments

Comments
 (0)