diff --git a/src/IsaacApiTypes.tsx b/src/IsaacApiTypes.tsx index 1adf6971d4..b59e7bb1de 100644 --- a/src/IsaacApiTypes.tsx +++ b/src/IsaacApiTypes.tsx @@ -657,9 +657,9 @@ export interface UserSummaryWithGroupMembershipDTO extends UserSummaryDTO { export interface IAssignmentLike { groupId?: number; id?: number; - creationDate?: Date; - dueDate?: Date; - scheduledStartDate?: Date; + creationDate?: Date | number; + dueDate?: Date | number; + scheduledStartDate?: Date | number; ownerUserId?: number; } diff --git a/src/app/components/elements/Assignments.tsx b/src/app/components/elements/Assignments.tsx index 496e26a7ae..2426cc3d14 100644 --- a/src/app/components/elements/Assignments.tsx +++ b/src/app/components/elements/Assignments.tsx @@ -3,20 +3,26 @@ import React, {useMemo, useState} from "react"; import {Button, Col, Collapse, Label, Row} from "reactstrap"; import {Link} from "react-router-dom"; import { + above, determineGameboardStagesAndDifficulties, determineGameboardSubjects, difficultyShortLabelMap, extractTeacherName, generateGameboardSubjectHexagons, + HUMAN_SUBJECTS, isDefined, PATHS, siteSpecific, stageLabelMap, TAG_ID, - tags + tags, + useDeviceSize } from "../../services"; -import {formatDate} from "./DateString"; +import {formatDate, FRIENDLY_DATE, getFriendlyDaysUntil} from "./DateString"; import {Circle} from "./svg/Circle"; +import { PhyHexIcon } from "./svg/PhyHexIcon"; +import { Subject } from "../../../IsaacAppTypes"; +import { HoverableTooltip } from "./HoverableTooltip"; const midnightOf = (date: Date | number) => { const d = new Date(date); @@ -41,6 +47,8 @@ const PhyAssignmentCard = ({assignment}: {assignment: AssignmentDTO}) => { const now = new Date(); const boardStagesAndDifficulties = useMemo(() => determineGameboardStagesAndDifficulties(assignment.gameboard), [assignment.gameboard]); + const deviceSize = useDeviceSize(); + const topics = tags.getTopicTags(Array.from((assignment.gameboard?.contents || []).reduce((a, c) => { if (isDefined(c.tags) && c.tags.length > 0) { return new Set([...Array.from(a), ...c.tags.map(id => id as TAG_ID)]); @@ -49,71 +57,90 @@ const PhyAssignmentCard = ({assignment}: {assignment: AssignmentDTO}) => { }, new Set())).filter(tag => isDefined(tag))).map(tag => tag.title).sort(); const assignmentStartDate = assignment.scheduledStartDate ?? assignment.creationDate; - return <> -
- - - -

{isDefined(assignment.gameboard) && assignment.gameboard.title}

- - {isDefined(assignmentStartDate) && -

Assigned: {formatDate(assignmentStartDate)}

- } - {isDefined(assignment.dueDate) && isDefined(assignment.gameboard) && now > midnightOf(assignment.dueDate) && assignment.gameboard.percentageAttempted !== 100 - ?

Overdue: {formatDate(assignment.dueDate)}

- : <>{assignment.dueDate &&

Due: {formatDate(assignment.dueDate)}

} - } - {isDefined(assignment.groupName) && -

Group: {assignment.groupName}

- } - {isDefined(assignment.assignerSummary) && -

By: {extractTeacherName(assignment.assignerSummary)}

- } - {isDefined(assignment.notes) &&

Notes: {assignment.notes}

} - - + const boardSubjects = determineGameboardSubjects(assignment.gameboard); - - - - - - - + return + + +
+
+
+ {generateGameboardSubjectHexagons(boardSubjects)} +
+ +
+
+

{isDefined(assignment.gameboard) && assignment.gameboard.title}

+ {above['sm'](deviceSize) && boardSubjects.length > 0 &&
+ {boardSubjects.map((subject) => {HUMAN_SUBJECTS[subject]})} +
} +
+
+ + + + {isDefined(assignmentStartDate) && +

+ Assigned{" "} + { getFriendlyDaysUntil(assignmentStartDate).startsWith("on ") + ? {getFriendlyDaysUntil(assignmentStartDate)} + : + {getFriendlyDaysUntil(assignmentStartDate)} + + } +

+ } + {isDefined(assignment.dueDate) && isDefined(assignment.gameboard) && now > midnightOf(assignment.dueDate) && assignment.gameboard.percentageAttempted !== 100 + ?

Overdue (due {formatDate(assignment.dueDate)})

+ : <>{assignment.dueDate &&

Due {getFriendlyDaysUntil(assignment.dueDate)}

} + } + {above['md'](deviceSize) && + {isDefined(assignment.groupName) && +

Group: {assignment.groupName}

+ } + {isDefined(assignment.assignerSummary) && +

By: {extractTeacherName(assignment.assignerSummary)}

+ } + }
+ + {assignment.notes &&

Notes: {assignment.notes}

} + + + + +
+ + +
+ {!above['md'](deviceSize) && + {isDefined(assignment.groupName) && +

Group: {assignment.groupName}

+ } + {isDefined(assignment.assignerSummary) && +

By: {extractTeacherName(assignment.assignerSummary)}

+ } + } +

Questions: {assignment.gameboard?.contents?.length || "0"}

{isDefined(topics) && topics.length > 0 &&

@@ -149,7 +176,7 @@ const PhyAssignmentCard = ({assignment}: {assignment: AssignmentDTO}) => { - ; + ; }; const CSAssignmentCard = ({assignment}: {assignment: AssignmentDTO}) => { diff --git a/src/app/components/elements/DateString.ts b/src/app/components/elements/DateString.ts index ab263d0f08..db7e82dc28 100644 --- a/src/app/components/elements/DateString.ts +++ b/src/app/components/elements/DateString.ts @@ -10,10 +10,10 @@ export const NUMERIC_DATE_AND_TIME = new Intl.DateTimeFormat(dateLocale, {year: // Wed, 22 Jan 2020, 13:00 export const FRIENDLY_DATE_AND_TIME = new Intl.DateTimeFormat(dateLocale, {weekday: "short", day: "numeric", month: "short", year: "numeric", hour: "numeric", minute: "2-digit", hour12: false}); -export function formatDate(date: number | Date | undefined) { +export function formatDate(date: number | Date | undefined, formatter=NUMERIC_DATE) { if (!date) return "Unknown"; const dateObject = new Date(date); - return NUMERIC_DATE.format(dateObject); + return formatter.format(dateObject); } // 2020-01-22 @@ -31,3 +31,34 @@ export const DateString = ({children, defaultValue, formatter=FRIENDLY_DATE_AND_ return fallback; } }; + +/** + * Calculates and returns a friendly string representing the number of days until a given date. + * If the date is in the future, return "today" if today, "tomorrow" if tomorrow, "in x days" if within a week, "on " if later. + * If the date is in the past, return "yesterday" if yesterday, "x days ago" if within a week, "on " if earlier. + * @param date the date to compare against + * @returns the friendly string. + */ +export function getFriendlyDaysUntil(date: number | Date) : string { + const today = new Date(); + + const getStartOfDay = (date: number | Date): Date => { + const dateObject = new Date(date); + dateObject.setHours(0, 0, 0, 0); + return dateObject; + }; + + const daysUntil = (getStartOfDay(date).getTime() - getStartOfDay(today).getTime()) / 86400000; + + if (daysUntil < 0) { + // in the past + if (daysUntil === -1) return "yesterday"; + if (daysUntil && daysUntil > -7) return `${-daysUntil} days ago`; + } else { + // in the future + if (daysUntil === 0) return "today"; + if (daysUntil === 1) return "tomorrow"; + if (daysUntil && daysUntil < 7) return `in ${daysUntil} days`; + } + return `on ${FRIENDLY_DATE.format(new Date(date))}`; +} diff --git a/src/app/components/elements/HoverableTooltip.tsx b/src/app/components/elements/HoverableTooltip.tsx new file mode 100644 index 0000000000..2f95aed90e --- /dev/null +++ b/src/app/components/elements/HoverableTooltip.tsx @@ -0,0 +1,17 @@ +import React, { useRef } from "react"; +import { UncontrolledTooltip } from "reactstrap"; + +interface HoverablePopupProps { + tooltip: string; +} + +export const HoverableTooltip = ({tooltip, children}: React.PropsWithChildren) => { + const ref = useRef(null); + + return

+ + {tooltip} + + {children} +
; +}; diff --git a/src/app/components/elements/layout/SidebarLayout.tsx b/src/app/components/elements/layout/SidebarLayout.tsx index ac28b03c8f..7fb2b97930 100644 --- a/src/app/components/elements/layout/SidebarLayout.tsx +++ b/src/app/components/elements/layout/SidebarLayout.tsx @@ -2,14 +2,16 @@ import React, { ChangeEvent, ReactNode, RefObject, useEffect, useRef, useState } import { Col, ColProps, RowProps, Input, Label, Offcanvas, OffcanvasBody, OffcanvasHeader, Row } from "reactstrap"; import partition from "lodash/partition"; import classNames from "classnames"; -import { ContentSummaryDTO, IsaacConceptPageDTO, QuestionDTO } from "../../../../IsaacApiTypes"; -import { above, AUDIENCE_DISPLAY_FIELDS, determineAudienceViews, filterAudienceViewsByProperties, getThemeFromContextAndTags, isAda, isDefined, siteSpecific, stageLabelMap, useDeviceSize } from "../../../services"; +import { AssignmentDTO, ContentSummaryDTO, IsaacConceptPageDTO, QuestionDTO } from "../../../../IsaacApiTypes"; +import { above, AUDIENCE_DISPLAY_FIELDS, determineAudienceViews, filterAssignmentsByStatus, filterAudienceViewsByProperties, getDistinctAssignmentGroups, getDistinctAssignmentSetters, getThemeFromContextAndTags, isAda, isDefined, siteSpecific, stageLabelMap, useDeviceSize } from "../../../services"; import { StageAndDifficultySummaryIcons } from "../StageAndDifficultySummaryIcons"; import { selectors, useAppSelector } from "../../../state"; import { Link } from "react-router-dom"; import { Tag } from "../../../../IsaacAppTypes"; import { AffixButton } from "../AffixButton"; import { getHumanContext } from "../../../services/pageContext"; +import { AssignmentState } from "../../pages/MyAssignments"; +import { ShowLoadingQuery } from "../../handlers/ShowLoadingQuery"; export const SidebarLayout = (props: RowProps) => { const { className, ...rest } = props; @@ -299,3 +301,93 @@ export const LessonsAndRevisionSidebar = (props: SidebarProps) => { // TODO return ; }; + +interface AssignmentStatusCheckboxProps extends React.HTMLAttributes { + status: AssignmentState; + statusFilter: AssignmentState[]; + setStatusFilter: React.Dispatch>; + count?: number; +} + +const AssignmentStatusCheckbox = (props: AssignmentStatusCheckboxProps) => { + const {status, statusFilter, setStatusFilter, count, ...rest} = props; + return !statusFilter.includes(status) ? setStatusFilter(c => [...c.filter(s => s !== AssignmentState.ALL), status]) : setStatusFilter(c => c.filter(s => s !== status))} + checked={statusFilter.includes(status)} + count={count} {...rest} + />; +}; + +const AssignmentStatusAllCheckbox = (props: Omit) => { + const { statusFilter, setStatusFilter, count, ...rest } = props; + const [previousFilters, setPreviousFilters] = useState([]); + return { + if (e.target.checked) { + setPreviousFilters(statusFilter); + setStatusFilter([AssignmentState.ALL]); + } else { + setStatusFilter(previousFilters); + } + }} + checked={statusFilter.includes(AssignmentState.ALL)} + count={count} {...rest} + />; +}; + +interface MyAssignmentsSidebarProps extends SidebarProps { + statusFilter: AssignmentState[]; + setStatusFilter: React.Dispatch>; + titleFilter: string; + setTitleFilter: React.Dispatch>; + groupFilter: string; + setGroupFilter: React.Dispatch>; + setByFilter: string; + setSetByFilter: React.Dispatch>; + assignmentQuery: any; +} + +export const MyAssignmentsSidebar = (props: MyAssignmentsSidebarProps) => { + const { statusFilter, setStatusFilter, titleFilter, setTitleFilter, groupFilter, setGroupFilter, setByFilter, setSetByFilter, assignmentQuery, ...rest } = props; + + useEffect(() => { + if (statusFilter.length === 0) { + setStatusFilter([AssignmentState.ALL]); + } + }, [statusFilter, setStatusFilter]); + + return + { + const myAssignments = filterAssignmentsByStatus(assignments); + const assignmentCountByStatus = myAssignments && Object.fromEntries(Object.entries(myAssignments).map(([key, value]) => [key, value.length])); + return <> +
+
Search assignments
+ ) => setTitleFilter(e.target.value)} + /> +
+
Filter by status
+ +
+ {Object.values(AssignmentState).filter(s => s !== AssignmentState.ALL).map(state => )} +
Filter by group
+ setGroupFilter(e.target.value)}> + {["All", ...getDistinctAssignmentGroups(assignments)].map(group => )} + +
Filter by assigner
+ setSetByFilter(e.target.value)}> + {["All", ...getDistinctAssignmentSetters(assignments)].map(setter => )} + + ; + }}/> + ; +}; diff --git a/src/app/components/elements/modals/ExtendDueDateModal.tsx b/src/app/components/elements/modals/ExtendDueDateModal.tsx index d8c160ee22..62476b4825 100644 --- a/src/app/components/elements/modals/ExtendDueDateModal.tsx +++ b/src/app/components/elements/modals/ExtendDueDateModal.tsx @@ -22,13 +22,13 @@ import { type ExtendDueDateModalProps = { isOpen: boolean; toggle: () => void; - currDueDate: Date; + currDueDate: Date | number; numericQuizAssignmentId: number; } export const ExtendDueDateModal = (props: ExtendDueDateModalProps) => { const {isOpen, toggle, currDueDate, numericQuizAssignmentId} = props; const yearRange = range(currentYear, currentYear + 5); - const [dueDate, setDueDate] = useState(currDueDate); + const [dueDate, setDueDate] = useState(new Date(currDueDate)); const [updateQuiz, {isLoading: isUpdatingQuiz}] = useUpdateQuizAssignmentMutation(); const dispatch = useAppDispatch(); diff --git a/src/app/components/elements/quiz/QuizProgressCommon.tsx b/src/app/components/elements/quiz/QuizProgressCommon.tsx index d111f0297c..3aa6d35e1c 100644 --- a/src/app/components/elements/quiz/QuizProgressCommon.tsx +++ b/src/app/components/elements/quiz/QuizProgressCommon.tsx @@ -42,7 +42,7 @@ export interface QuestionType { export interface ResultsTableProps { assignmentId?: number; - duedate?: Date; + duedate?: Date | number; progress?: AssignmentProgressDTO[]; questions: Q[]; header: JSX.Element; diff --git a/src/app/components/pages/MyAssignments.tsx b/src/app/components/pages/MyAssignments.tsx index 1f7a0bab11..739eed2fab 100644 --- a/src/app/components/pages/MyAssignments.tsx +++ b/src/app/components/pages/MyAssignments.tsx @@ -14,11 +14,12 @@ import { import {Assignments} from "../elements/Assignments"; import {ShowLoadingQuery} from "../handlers/ShowLoadingQuery"; import {PageFragment} from "../elements/PageFragment"; +import { MainContent, MyAssignmentsSidebar, SidebarLayout } from "../elements/layout/SidebarLayout"; const INITIAL_NO_ASSIGNMENTS = 10; const NO_ASSIGNMENTS_INCREMENT = 10; -enum AssignmentState { +export enum AssignmentState { ALL = "All", TODO_RECENT = "To do (recent)", TODO_OLDER = "To do (older)", @@ -26,7 +27,78 @@ enum AssignmentState { ALL_CORRECT = "All correct" } -export const MyAssignments = ({user}: {user: RegisteredUserDTO}) => { +const PhyMyAssignments = ({user}: {user: RegisteredUserDTO}) => { + const dispatch = useAppDispatch(); + useEffect(() => {dispatch(logAction({type: "VIEW_MY_ASSIGNMENTS"}));}, [dispatch]); + + // TODO don't refetch "my assignments" every component mount, an instead invalidate cache when actions occur + // that require refetching. + const assignmentQuery = useGetMyAssignmentsQuery(undefined, {refetchOnMountOrArgChange: true, refetchOnReconnect: true}); + + const [assignmentStateFilter, setAssignmentStateFilter] = useState([AssignmentState.ALL]); + const [assignmentTitleFilter, setAssignmentTitleFilter] = useState(""); + const [assignmentSetByFilter, setAssignmentSetByFilter] = useState("All"); + const [assignmentGroupFilter, setAssignmentGroupFilter] = useState("All"); + + const [limit, setLimit] = useState(INITIAL_NO_ASSIGNMENTS); + + const pageHelp = + Any {siteSpecific("assignments", "quizzes")} you have been set will appear here.
+ Overdue {siteSpecific("assignments", "quizzes")} which have not been fully attempted will be treated as {siteSpecific("assignments", "quizzes")} To do until they are due, + after which they are considered Older {siteSpecific("assignments", "quizzes")}. +
; + + return + + } /> + + + + { + const myAssignments = filterAssignmentsByStatus(assignments); + + const assignmentByStates: Record = { + [AssignmentState.ALL]: [...myAssignments.inProgressRecent, ...myAssignments.inProgressOld, ...myAssignments.allAttempted, ...myAssignments.allCorrect], + [AssignmentState.TODO_RECENT]: myAssignments.inProgressRecent, + [AssignmentState.TODO_OLDER]: myAssignments.inProgressOld, + [AssignmentState.ALL_ATTEMPTED]: myAssignments.allAttempted, + [AssignmentState.ALL_CORRECT]: myAssignments.allCorrect + }; + + const filteredAssignments = filterAssignmentsByProperties( + assignmentStateFilter.includes(AssignmentState.ALL) ? assignmentByStates[AssignmentState.ALL] : assignmentStateFilter.flatMap(f => assignmentByStates[f]), + assignmentTitleFilter, assignmentGroupFilter, assignmentSetByFilter + ); + + return
+ + {limit < filteredAssignments.length &&
+
+

+ Showing {limit} of {filteredAssignments.length} filtered {siteSpecific("assignments", "quizzes")}. +

+ +
} +
; + }} + /> +
+
+
; +}; + +const AdaMyAssignments = ({user}: {user: RegisteredUserDTO}) => { const dispatch = useAppDispatch(); useEffect(() => {dispatch(logAction({type: "VIEW_MY_ASSIGNMENTS"}));}, [dispatch]); @@ -51,7 +123,7 @@ export const MyAssignments = ({user}: {user: RegisteredUserDTO}) => { } /> - + { const filteredAssignments = filterAssignmentsByProperties(assignmentByStates[assignmentStateFilter], assignmentTitleFilter, assignmentGroupFilter, assignmentSetByFilter); return <> - + - + @@ -121,3 +193,5 @@ export const MyAssignments = ({user}: {user: RegisteredUserDTO}) => { ; }; + +export const MyAssignments = siteSpecific(PhyMyAssignments, AdaMyAssignments); diff --git a/src/app/components/pages/quizzes/SetQuizzes.tsx b/src/app/components/pages/quizzes/SetQuizzes.tsx index 4980423bc2..1ab84921fe 100644 --- a/src/app/components/pages/quizzes/SetQuizzes.tsx +++ b/src/app/components/pages/quizzes/SetQuizzes.tsx @@ -80,7 +80,7 @@ const _compareStrings = (a: string | undefined, b: string | undefined): number = return a.localeCompare(b); }; -const _compareDates = (a: Date | undefined, b: Date | undefined): number => { +const _compareDates = (a: Date | number | undefined, b: Date | number | undefined): number => { // sorts by date descending (most recent first), then undefined if (!a && !b) return 0; if (!a) return 1; diff --git a/src/app/services/assignments.ts b/src/app/services/assignments.ts index 275562480f..5ab26f77dc 100644 --- a/src/app/services/assignments.ts +++ b/src/app/services/assignments.ts @@ -10,7 +10,7 @@ export function getAssignmentCSVDownloadLink(assignmentId: number) { return `${API_PATH}/assignments/assign/${assignmentId}/progress/download`; } -function createAssignmentWithStartDate(assignment: AssignmentDTO): AssignmentDTO & {startDate: Date} { +function createAssignmentWithStartDate(assignment: AssignmentDTO): AssignmentDTO & {startDate: Date | number} { const assignmentStartDate = assignment.scheduledStartDate ?? assignment.creationDate as Date; return {...assignment, startDate: assignmentStartDate}; } @@ -23,7 +23,7 @@ export const filterAssignmentsByStatus = (assignments: AssignmentDTO[] | undefin const midnightLastNight = new Date(now); midnightLastNight.setHours(0, 0, 0, 0); - const myAssignments: Record = { + const myAssignments: Record = { inProgressRecent: [], inProgressOld: [], allAttempted: [], diff --git a/src/app/services/quiz.ts b/src/app/services/quiz.ts index 5679561226..0535f118fc 100644 --- a/src/app/services/quiz.ts +++ b/src/app/services/quiz.ts @@ -301,11 +301,11 @@ export interface DisplayableQuiz { id: string | number; isAssigned: boolean; title?: string; - creationDate?: Date; - startDate?: Date; - setDate?: Date; - dueDate?: Date; - completedDate?: Date; + creationDate?: Date | number; + startDate?: Date | number; + setDate?: Date | number; + dueDate?: Date | number; + completedDate?: Date | number; attempt?: QuizAttemptDTO; assignerSummary?: UserSummaryDTO; quizFeedbackMode?: QuizFeedbackMode; diff --git a/src/app/state/slices/api/assignments.ts b/src/app/state/slices/api/assignments.ts index d354b2e52e..1e26450497 100644 --- a/src/app/state/slices/api/assignments.ts +++ b/src/app/state/slices/api/assignments.ts @@ -32,7 +32,7 @@ export const useGroupAssignmentSummary = (user: RegisteredUserDTO, groupId?: num }; // This looks a bit odd, but it means that we can use the same sort function for both gameboard and quiz assignments -type SortFuncInputType = {creationDate?: Date, dueDate?: Date, scheduledStartDate?: Date, gameboard?: GameboardDTO, quiz?: IsaacQuizDTO}; +type SortFuncInputType = {creationDate?: Date | number, dueDate?: Date | number, scheduledStartDate?: Date | number, gameboard?: GameboardDTO, quiz?: IsaacQuizDTO}; const sortAssignments = (assignments: SortFuncInputType[] | undefined, sortOrder?: AssignmentOrderSpec) => { let sortedAssignments; switch (sortOrder?.type) { diff --git a/src/scss/common/elements.scss b/src/scss/common/elements.scss index 14a4e19588..f9aabd7d68 100644 --- a/src/scss/common/elements.scss +++ b/src/scss/common/elements.scss @@ -346,3 +346,13 @@ iframe.email-html { border-left: 1px solid rgba(0, 0, 0, 0.1); opacity: 1; } + +.hoverable-tooltip { + position: relative; + display: inline-block; + cursor: pointer; + text-decoration: underline dotted; + &:hover .popup { + display: block; + } +} diff --git a/src/scss/common/forms.scss b/src/scss/common/forms.scss index dcfbe573d6..8f6282013f 100644 --- a/src/scss/common/forms.scss +++ b/src/scss/common/forms.scss @@ -78,18 +78,6 @@ input { } } -select { - &.form-select { - border: solid 1px $black; - height: 48px; - padding: 0 1.2em; - - &[name='selectMulti'] { - padding: 1rem 1.2rem 1.5rem; - } - } -} - textarea { &.form-control { border: solid 1px $black; diff --git a/src/scss/cs/forms.scss b/src/scss/cs/forms.scss index b1b06705d1..5febfd76e9 100644 --- a/src/scss/cs/forms.scss +++ b/src/scss/cs/forms.scss @@ -115,3 +115,14 @@ select.form-select, input.form-control, .separate-input-group > .form-control + border: none; } } + +select { + &.form-select { + border: solid 1px $black; + padding: 0 1.2em; + + &[name='selectMulti'] { + padding: 1rem 1.2rem 1.5rem; + } + } +} diff --git a/src/scss/phy/assignments.scss b/src/scss/phy/assignments.scss new file mode 100644 index 0000000000..3106f23cbf --- /dev/null +++ b/src/scss/phy/assignments.scss @@ -0,0 +1,21 @@ +.assignments-card { + background-color: white; + text-decoration: none; + border: 1px solid $color-neutral-300; + border-radius: $border-radius-lg; + transition: all 0.2s ease-out; + + &:hover { + border-color: $color-neutral-400; + scale: 1.02; + } + + .overdue { + color: $color-misc-red; + font-weight: 600; + } +} + +.assignment-hex { + z-index: 5; +} diff --git a/src/scss/phy/boards.scss b/src/scss/phy/boards.scss index 8652d70c92..7414ba7e6c 100644 --- a/src/scss/phy/boards.scss +++ b/src/scss/phy/boards.scss @@ -1,9 +1,8 @@ @import "../common/boards"; .board-subject-hexagon-size { - width: 63px; - min-width: 63px; - height: 74px; + width: 72px; + height: 72px; } // Subject hexagons: @@ -45,11 +44,7 @@ font-size: 30px; line-height: 110%; font-family: $secondary-font-semi-bold; - color: #fff; - z-index: 10; - position: absolute; - top: 20px; - left: 5px; + color: $color-neutral-900; text-align: center; &:after { font-size: 0.5em; @@ -60,21 +55,25 @@ } .board-subject-hexagon { - background: url(/assets/phy/icons/hexagons_sprite.png) no-repeat -256px 0; - left: 0; + // background: url(/assets/phy/icons/hexagons_sprite.png) no-repeat -256px 0; + // left: 0; + mask: url(/assets/phy/icons/redesign/hexagon.svg) no-repeat center center; + mask-size: contain; + width: 72px; + height: 72px; } .subject-physics { - background-position: 0 0; + background-color: $color-physics-500; } .subject-maths { - background-position: -64px 0; + background-color: $color-maths-500; } .subject-chemistry { - background-position: -128px 0; + background-color: $color-chemistry-500; } .subject-biology { - background-position: -192px 0; -} -.subject-complete { - background-position: -320px 0; + background-color: $color-biology-500; } +// .subject-complete { +// background-position: -320px 0; +// } diff --git a/src/scss/phy/color-theme.scss b/src/scss/phy/color-theme.scss index 29838064e8..90fff055a5 100644 --- a/src/scss/phy/color-theme.scss +++ b/src/scss/phy/color-theme.scss @@ -139,6 +139,10 @@ } } +.bg-theme { + background-color: var(--subject-color-500); +} + .border-theme { border-color: var(--subject-color-500); } diff --git a/src/scss/phy/forms.scss b/src/scss/phy/forms.scss index 0c90ca52ae..5f5a911d42 100644 --- a/src/scss/phy/forms.scss +++ b/src/scss/phy/forms.scss @@ -41,10 +41,17 @@ } } -.search--filter-input { +[type='search'].search--filter-input { border: 1px solid $color-neutral-300; } +select.form-select { + border: none; + border-radius: 0; + border-bottom: 2px solid $color-neutral-300; + background-color: transparent; +} + .form-check-input { width: 24px; height: 24px; diff --git a/src/scss/phy/icons.scss b/src/scss/phy/icons.scss index ab122d22b9..3d70007ffb 100644 --- a/src/scss/phy/icons.scss +++ b/src/scss/phy/icons.scss @@ -179,6 +179,14 @@ ); } +.page-icon-question-pack { + @include svg-icon-layered( + '/assets/phy/icons/redesign/question-pack-fg.svg', + '/assets/phy/icons/redesign/question-pack-bg.svg', + 75px, 75px, 44px + ); +} + .page-icon-finder { @include svg-icon-layered( '/assets/phy/icons/redesign/page-finder-fg.svg', diff --git a/src/scss/phy/isaac.scss b/src/scss/phy/isaac.scss index 3b864e8b27..39207ee82d 100644 --- a/src/scss/phy/isaac.scss +++ b/src/scss/phy/isaac.scss @@ -63,7 +63,6 @@ $shadow-black: rgb(0, 0, 0); // Button sizing $input-btn-padding-y-lg: 0.5rem; $input-btn-padding-x-lg: 3rem; -$border-radius-lg: 0.25rem; // fonts $primary-font: 'hanken-grotesk', Arial, 'Helvetica Neue', Helvetica, sans-serif; @@ -410,6 +409,7 @@ $theme-colors-border-subtle: map-merge($theme-colors-border-subtle, $custom-colo @import "../common/events"; @import "../common/fasttrack"; @import "../common/scroll"; +@import "assignments"; @import "assignment-progress"; @import "books"; @import "menu-page-phy"; diff --git a/src/test/services/assignments.test.ts b/src/test/services/assignments.test.ts index 2d2caf9012..33327b3ed9 100644 --- a/src/test/services/assignments.test.ts +++ b/src/test/services/assignments.test.ts @@ -94,7 +94,7 @@ const fullyAttemptedQuestion = {...threePartQuestion, id: "fully_attempted_quest const partiallyAttemptedQuestion = {...threePartQuestion, id: "partially_attempted_question", questionPartsCorrect: 1, questionPartsIncorrect: 1, questionPartsNotAttempted: 1}; const notAttemptedQuestion = {...threePartQuestion, id: "not_attempted_question", questionPartsNotAttempted: 3}; -function createAssignmentWithStartDate(assignment: AssignmentDTO): AssignmentDTO & {startDate: Date} { +function createAssignmentWithStartDate(assignment: AssignmentDTO): AssignmentDTO & {startDate: Date | number} { const assignmentStartDate = assignment.scheduledStartDate ?? assignment.creationDate as Date; return {...assignment, startDate: assignmentStartDate}; }