Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 | 325x 141x 141x 141x 141x 28x 80x 14x 19x 43x 43x 40x | /**
* Assignment open/close status, shared by the teacher assignment list and the
* assignment detail page.
*
* `assignmentStatus` was inline in
* `app/teacher-page/page/assignments/AssignmentsPage.jsx`, where it
* drives both the status chip and the "Data Analytics" gate. The detail page
* (`view-assignment/AssignmentSettings.jsx`) had its own, incompatible date
* comparison for the same button — see `hasAssignmentOpened` below. Sharing one
* implementation is what stops the two drifting again.
*
* Dates arrive from the API as ISO strings WITHOUT a zone marker but are stored
* as UTC, hence `{ zone: "utc" }` on every parse. Comparisons are between
* instants, never between calendar days.
*
* Developer: Allan Ninal
*/
import { DateTime } from "luxon";
/**
* Parse an API date as UTC.
*
* An UNPARSEABLE string deliberately yields luxon's Invalid DateTime rather than
* null. It is truthy, and every comparison against it is false (its valueOf is
* NaN) — which is what makes `assignmentStatus` classify a garbage date as
* "INVALID" rather than falling through to "UNDEPLOYED". Normalising it to null
* here would change that, and the status chip renders the result.
*/
function toUTC(dateString) {
return dateString ? DateTime.fromISO(dateString, { zone: "utc" }) : null;
}
/**
* Classify an assignment's window.
*
* Moved verbatim in behaviour from Main.jsx, including the cases that look odd:
* "ASSIGNED" and "UNDEPLOYED" both require BOTH dates, so an assignment with a
* date_open but no date_close falls through to "INVALID". That is preserved
* deliberately — the status chip renders these strings today, and changing the
* classification would change what teachers see in the list.
*
* @param {string|null|undefined} dateOpen
* @param {string|null|undefined} dateClose
* @returns {"CLOSED"|"ASSIGNED"|"UNDEPLOYED"|"INVALID"}
*/
export function assignmentStatus(dateOpen, dateClose) {
const todayUTC = DateTime.utc();
const dateOpenUTC = toUTC(dateOpen);
const dateCloseUTC = toUTC(dateClose);
switch (true) {
case Boolean(dateCloseUTC) && dateCloseUTC < todayUTC:
return "CLOSED";
case Boolean(dateOpenUTC) && Boolean(dateCloseUTC) && dateOpenUTC <= todayUTC && todayUTC < dateCloseUTC:
return "ASSIGNED";
case (!dateOpenUTC && !dateCloseUTC) || (dateOpenUTC > todayUTC && dateCloseUTC > todayUTC):
return "UNDEPLOYED";
default:
return "INVALID";
}
}
/**
* Has the assignment's open date passed?
*
* Replaces this, which the detail page used to gate its Data Analytics button:
*
* const today = new Date();
* today.setHours(0, 0, 0, 0); // LOCAL midnight...
* return today >= new Date(dateOpen); // ...compared to a UTC instant
*
* Flattening to local midnight makes the comparison point EARLIER than now, by
* up to a day plus the UTC offset — so the button stayed hidden long after the
* assignment had opened. Measured in UTC+8: an assignment that opened 10 minutes
* ago reported `false`.
*
* This compares instants, so it flips exactly when the assignment opens.
*
* @param {string|null|undefined} dateOpen
* @returns {boolean}
*/
export function hasAssignmentOpened(dateOpen) {
const dateOpenUTC = toUTC(dateOpen);
if (!dateOpenUTC) return false;
// An unparseable date needs no special case: luxon's Invalid DateTime has a
// NaN valueOf, so this comparison is false — verified, and an explicit
// `.isValid` guard here was redundant (a mutation removing it killed no test).
return dateOpenUTC <= DateTime.utc();
}
|