1
1
import { AssignmentDTO } from "../../../IsaacApiTypes" ;
2
- import React , { useMemo , useState } from "react" ;
3
- import { Button , Col , Collapse , Label , Row } from "reactstrap" ;
2
+ import React , { } from "react" ;
3
+ import { Col , Label , Row } from "reactstrap" ;
4
4
import { Link } from "react-router-dom" ;
5
5
import {
6
- above ,
7
- determineGameboardStagesAndDifficulties ,
8
- determineGameboardSubjects ,
9
- difficultyShortLabelMap ,
10
6
extractTeacherName ,
11
- generateGameboardSubjectHexagons ,
12
- HUMAN_SUBJECTS ,
13
7
isDefined ,
14
8
PATHS ,
15
- siteSpecific ,
16
- stageLabelMap ,
17
- TAG_ID ,
18
- tags ,
19
- useDeviceSize
20
- } from "../../services" ;
21
- import { formatDate , FRIENDLY_DATE , getFriendlyDaysUntil } from "./DateString" ;
9
+ siteSpecific } from "../../services" ;
10
+ import { formatDate , getFriendlyDaysUntil } from "./DateString" ;
22
11
import { Circle } from "./svg/Circle" ;
23
- import { PhyHexIcon } from "./svg/PhyHexIcon" ;
24
- import { Subject } from "../../../IsaacAppTypes" ;
25
- import { HoverableTooltip } from "./HoverableTooltip" ;
12
+ import { GameboardCard , GameboardLinkLocation } from "./cards/GameboardCard" ;
26
13
27
14
const midnightOf = ( date : Date | number ) => {
28
15
const d = new Date ( date ) ;
@@ -43,140 +30,34 @@ const CSCircle = ({label, percentage}: {percentage: number | unknown, label: str
43
30
} ;
44
31
45
32
const PhyAssignmentCard = ( { assignment} : { assignment : AssignmentDTO } ) => {
46
- const [ showMore , setShowMore ] = useState ( false ) ;
47
33
const now = new Date ( ) ;
48
- const boardStagesAndDifficulties = useMemo ( ( ) => determineGameboardStagesAndDifficulties ( assignment . gameboard ) , [ assignment . gameboard ] ) ;
49
-
50
- const deviceSize = useDeviceSize ( ) ;
51
-
52
- const topics = tags . getTopicTags ( Array . from ( ( assignment . gameboard ?. contents || [ ] ) . reduce ( ( a , c ) => {
53
- if ( isDefined ( c . tags ) && c . tags . length > 0 ) {
54
- return new Set ( [ ...Array . from ( a ) , ...c . tags . map ( id => id as TAG_ID ) ] ) ;
55
- }
56
- return a ;
57
- } , new Set < TAG_ID > ( ) ) ) . filter ( tag => isDefined ( tag ) ) ) . map ( tag => tag . title ) . sort ( ) ;
58
34
const assignmentStartDate = assignment . scheduledStartDate ?? assignment . creationDate ;
59
35
60
- const boardSubjects = determineGameboardSubjects ( assignment . gameboard ) ;
61
-
62
- return < Link className = "w-100 assignments-card px-3 py-2 mb-3" to = { `${ PATHS . GAMEBOARD } #${ assignment . gameboardId } ` } >
63
- < Row data-testid = "my-assignment" >
64
- < Col xs = { 8 } >
65
- < div className = "d-flex align-items-center" >
66
- < div className = "d-flex justify-content-center board-subject-hexagon-size me-4 my-2" >
67
- < div className = "board-subject-hexagon-container justify-content-center" >
68
- { generateGameboardSubjectHexagons ( boardSubjects ) }
69
- </ div >
70
- < PhyHexIcon icon = "page-icon-question-pack" subject = { boardSubjects [ 0 ] as Subject } className = "assignment-hex ps-3" />
71
- </ div >
72
- < div className = "d-flex flex-column flex-grow-1" >
73
- < h4 className = "text-break m-0" > { isDefined ( assignment . gameboard ) && assignment . gameboard . title } </ h4 >
74
- { above [ 'sm' ] ( deviceSize ) && boardSubjects . length > 0 && < div className = "d-flex align-items-center mb-2" >
75
- { boardSubjects . map ( ( subject ) => < span key = { subject } className = "badge rounded-pill bg-theme me-1" data-bs-theme = { subject } > { HUMAN_SUBJECTS [ subject ] } </ span > ) }
76
- </ div > }
77
- </ div >
78
- </ div >
79
-
80
- < Row >
81
- < Col >
82
- { isDefined ( assignmentStartDate ) &&
83
- < p className = "mb-0" data-testid = { "gameboard-assigned" } >
84
- Assigned{ " " }
85
- { getFriendlyDaysUntil ( assignmentStartDate ) . startsWith ( "on " )
86
- ? < strong > { getFriendlyDaysUntil ( assignmentStartDate ) } </ strong >
87
- : < HoverableTooltip tooltip = { formatDate ( assignmentStartDate , FRIENDLY_DATE ) } >
88
- < strong > { getFriendlyDaysUntil ( assignmentStartDate ) } </ strong >
89
- </ HoverableTooltip >
90
- }
91
- </ p >
92
- }
93
- { isDefined ( assignment . dueDate ) && isDefined ( assignment . gameboard ) && now > midnightOf ( assignment . dueDate ) && assignment . gameboard . percentageAttempted !== 100
94
- ? < p className = "mb-0" > < strong className = "overdue" > Overdue</ strong > < span className = "small text-muted" > (due { formatDate ( assignment . dueDate ) } )</ span > </ p >
95
- : < > { assignment . dueDate && < p className = "mb-0" > Due < strong > { getFriendlyDaysUntil ( assignment . dueDate ) } </ strong > </ p > } </ >
96
- }
97
- </ Col >
98
- { above [ 'md' ] ( deviceSize ) && < Col >
99
- { isDefined ( assignment . groupName ) &&
100
- < p className = "mb-0" > < strong > Group:</ strong > { assignment . groupName } </ p >
101
- }
102
- { isDefined ( assignment . assignerSummary ) &&
103
- < p className = "mb-0" > < strong > By:</ strong > { extractTeacherName ( assignment . assignerSummary ) } </ p >
104
- }
105
- </ Col > }
106
- </ Row >
107
-
108
- { assignment . notes && < p className = "mb-0" > < strong > Notes:</ strong > { assignment . notes } </ p > }
109
- < Button className = "my-2 btn-underline" color = "link" onClick = { ( e ) => { e . preventDefault ( ) ; setShowMore ( ! showMore ) ; } } >
110
- { showMore ? "Hide details" : "Show details" }
111
- </ Button >
36
+ return < GameboardCard gameboard = { assignment . gameboard } linkLocation = { GameboardLinkLocation . Card } >
37
+ < Row className = "w-100" >
38
+ < Col xs = { 12 } md = { 6 } >
39
+ { isDefined ( assignmentStartDate ) &&
40
+ < p className = "mb-0" data-testid = { "gameboard-assigned" } >
41
+ Assigned < strong > { getFriendlyDaysUntil ( assignmentStartDate ) } </ strong >
42
+ </ p >
43
+ }
44
+ { isDefined ( assignment . dueDate ) && isDefined ( assignment . gameboard ) && now > midnightOf ( assignment . dueDate ) && assignment . gameboard . percentageAttempted !== 100
45
+ ? < p className = "mb-0" > < strong className = "overdue" > Overdue</ strong > < span className = "small text-muted" > (due { formatDate ( assignment . dueDate ) } )</ span > </ p >
46
+ : < > { assignment . dueDate && < p className = "mb-0" > Due < strong > { getFriendlyDaysUntil ( assignment . dueDate ) } </ strong > </ p > } </ >
47
+ }
112
48
</ Col >
113
-
114
- < Col xs = { 4 } >
115
- < div className = "d-flex flex-wrap justify-content-center justify-content-md-end justify-content-lg-center justify-content-xl-end column-gap-4" >
116
- < Label className = "d-block w-max-content text-center text-nowrap pt-3" >
117
- { isDefined ( assignment . gameboard ) && ( ( assignment . gameboard . percentageAttempted === 100 ) ?
118
- < span className = "board-subject-hexagon subject-complete" /> :
119
- < div className = "board-percent-completed" > { assignment . gameboard . percentageAttempted ?? 0 } </ div >
120
- ) }
121
- Attempted
122
- </ Label >
123
- < Label className = "d-block w-max-content text-center text-nowrap pt-3" >
124
- { isDefined ( assignment . gameboard ) && ( ( assignment . gameboard . percentageCorrect === 100 ) ?
125
- < span className = "board-subject-hexagon subject-complete" /> :
126
- < div className = "board-percent-completed" > { assignment . gameboard . percentageCorrect ?? 0 } </ div >
127
- ) }
128
- Correct
129
- </ Label >
130
- </ div >
49
+ < Col >
50
+ { isDefined ( assignment . groupName ) &&
51
+ < p className = "mb-0" > < strong > Group:</ strong > { assignment . groupName } </ p >
52
+ }
53
+ { isDefined ( assignment . assignerSummary ) &&
54
+ < p className = "mb-0" > < strong > By:</ strong > { extractTeacherName ( assignment . assignerSummary ) } </ p >
55
+ }
131
56
</ Col >
132
57
</ Row >
133
- < Collapse isOpen = { showMore } className = "w-100" >
134
- < Row >
135
- { ! above [ 'md' ] ( deviceSize ) && < Col xs = { 12 } >
136
- { isDefined ( assignment . groupName ) &&
137
- < p className = "mb-0" > < strong > Group:</ strong > { assignment . groupName } </ p >
138
- }
139
- { isDefined ( assignment . assignerSummary ) &&
140
- < p className = "mb-0" > < strong > By:</ strong > { extractTeacherName ( assignment . assignerSummary ) } </ p >
141
- }
142
- </ Col > }
143
-
144
- < Col xs = { 12 } md = { 8 } className = "mt-sm-2" >
145
- < p className = "mb-0" > < strong > Questions:</ strong > { assignment . gameboard ?. contents ?. length || "0" } </ p >
146
- { isDefined ( topics ) && topics . length > 0 && < p className = "mb-0" >
147
- < strong > { topics . length === 1 ? "Topic" : "Topics" } :</ strong > { " " }
148
- { topics . join ( ", " ) }
149
- </ p > }
150
- </ Col >
151
- < Col xs = { 12 } md = { 4 } className = "mt-sm-2" >
152
- { boardStagesAndDifficulties . length > 0 && < p className = "mb-0" >
153
- < table className = "w-100" >
154
- < thead >
155
- < tr >
156
- < th className = "w-50" >
157
- { `Stage${ boardStagesAndDifficulties . length > 1 ? "s" : "" } :` }
158
- </ th >
159
- < th className = "w-50" >
160
- { `Difficult${ boardStagesAndDifficulties . some ( ( [ , ds ] ) => ds . length > 1 ) ? "ies" : "y" } ` }
161
- </ th >
162
- </ tr >
163
- </ thead >
164
- < tbody >
165
- { boardStagesAndDifficulties . map ( ( [ stage , difficulties ] ) => < tr key = { stage } >
166
- < td className = "w-50 align-baseline" >
167
- { stageLabelMap [ stage ] } :
168
- </ td >
169
- < td className = "w-50 ps-1" >
170
- { difficulties . map ( ( d ) => difficultyShortLabelMap [ d ] ) . join ( ", " ) }
171
- </ td >
172
- </ tr > ) }
173
- </ tbody >
174
- </ table >
175
- </ p > }
176
- </ Col >
177
- </ Row >
178
- </ Collapse >
179
- </ Link > ;
58
+
59
+ { assignment . notes && < p className = "mb-0" > < strong > Notes:</ strong > { assignment . notes } </ p > }
60
+ </ GameboardCard > ;
180
61
} ;
181
62
182
63
const CSAssignmentCard = ( { assignment} : { assignment : AssignmentDTO } ) => {
0 commit comments