diff --git a/frontend/src/views/admin/matching/applicant-view/body.tsx b/frontend/src/views/admin/matching/applicant-view/body.tsx index 0319582e..f369eba6 100644 --- a/frontend/src/views/admin/matching/applicant-view/body.tsx +++ b/frontend/src/views/admin/matching/applicant-view/body.tsx @@ -48,6 +48,7 @@ export function ApplicantViewBody({ applicantSummaries={applicantSummaries} /> )} +
{applicantCountString}
); diff --git a/frontend/src/views/admin/matching/applicant-view/grid/modals.tsx b/frontend/src/views/admin/matching/applicant-view/grid/modals.tsx index 9015a43f..11571252 100644 --- a/frontend/src/views/admin/matching/applicant-view/grid/modals.tsx +++ b/frontend/src/views/admin/matching/applicant-view/grid/modals.tsx @@ -1,7 +1,7 @@ import React from "react"; import { Application } from "../../../../../api/defs/types"; import { MatchableAssignment } from "../../types"; -import { Form, Modal, Button } from "react-bootstrap"; +import { Modal, Button } from "react-bootstrap"; import { ApplicationDetails } from "../../../applications/application-details"; import { upsertMatch } from "../../actions"; import { useThunkDispatch } from "../../../../../libs/thunk-dispatch"; @@ -67,19 +67,16 @@ export function AdjustHourModal({ Update Hours -
- - 0 - ? match.hoursAssigned - : 0 - } - onChange={(e) => setHoursAssigned(e.target.value)} - /> - -
+ 0 + ? match.hoursAssigned + : 0 + } + onChange={(e) => setHoursAssigned(e.target.value)} + />
- + setAddDialog(false)}> - Import Appointment Guarantee Data + + Import Subsequent Appointment Data +
diff --git a/frontend/src/views/admin/matching/import-export/import-matching-data.tsx b/frontend/src/views/admin/matching/import-export/import-matching-data.tsx index ebdf646a..352a6936 100644 --- a/frontend/src/views/admin/matching/import-export/import-matching-data.tsx +++ b/frontend/src/views/admin/matching/import-export/import-matching-data.tsx @@ -143,7 +143,7 @@ export function ImportMatchingDataButton() { > Import Data - + setAddDialog(false)}> Import Matching Data diff --git a/frontend/src/views/admin/matching/position-list.tsx b/frontend/src/views/admin/matching/position-list.tsx index 9676c7d3..fcf4e8d0 100644 --- a/frontend/src/views/admin/matching/position-list.tsx +++ b/frontend/src/views/admin/matching/position-list.tsx @@ -1,9 +1,9 @@ import React from "react"; +import classNames from "classnames"; import { useThunkDispatch } from "../../../libs/thunk-dispatch"; import { round } from "../../../libs/utils"; import { PositionSummary } from "./types"; import { setSelectedMatchingPosition } from "./actions"; -import { Form } from "react-bootstrap"; /** * A searchable list of position codes. @@ -36,15 +36,14 @@ export function PositionList({ return (
- - + setFilterString(e.target.value)} /> - +
{filteredList.map((summary) => ( @@ -92,7 +91,12 @@ function PositionRow({ return (
dispatch( setSelectedMatchingPosition(positionSummary.position.id) @@ -100,9 +104,7 @@ function PositionRow({ } >
@@ -118,6 +120,15 @@ function PositionRow({ {positionSummary.hoursAssigned} / {targetHours} h + {focused && ( + + {positionSummary.applicantSummaries.length}{" "} + applicant + {positionSummary.applicantSummaries.length === 1 + ? "" + : "s"} + + )}
diff --git a/frontend/src/views/admin/matching/sorts/sorts.ts b/frontend/src/views/admin/matching/sorts/sorts.ts index b88d863f..698e5c5f 100644 --- a/frontend/src/views/admin/matching/sorts/sorts.ts +++ b/frontend/src/views/admin/matching/sorts/sorts.ts @@ -47,7 +47,7 @@ export function applySorts( ) { // Return early if any inputs aren't defined if (applicantSummaries.length === 0 || sortList.length === 0) { - return []; + return [...applicantSummaries]; } const ret: ApplicantSummary[] = [...applicantSummaries]; @@ -77,7 +77,7 @@ function flipIfDescending(val: number, asc: boolean) { // Sorting functions -- all of these do in-place sorting function sortByFirstName(applicantSummaries: ApplicantSummary[], asc = true) { applicantSummaries.sort((a, b) => { - return `${a.applicant.first_name}, ${a.applicant.last_name}`.toLowerCase() < + return `${a.applicant.first_name}, ${a.applicant.last_name}`.toLowerCase() <= `${b.applicant.first_name}, ${b.applicant.last_name}`.toLowerCase() ? flipIfDescending(-1, asc) : flipIfDescending(1, asc); @@ -86,7 +86,7 @@ function sortByFirstName(applicantSummaries: ApplicantSummary[], asc = true) { function sortByLastName(applicantSummaries: ApplicantSummary[], asc = true) { applicantSummaries.sort((a, b) => { - return `${a.applicant.last_name}, ${a.applicant.first_name}`.toLowerCase() < + return `${a.applicant.last_name}, ${a.applicant.first_name}`.toLowerCase() <= `${b.applicant.last_name}, ${b.applicant.first_name}`.toLowerCase() ? flipIfDescending(-1, asc) : flipIfDescending(1, asc); @@ -128,18 +128,18 @@ function sortByProgram(applicantSummaries: ApplicantSummary[], asc = true) { function sortByGpa(applicantSummaries: ApplicantSummary[], asc = true) { applicantSummaries.sort((a, b) => { if (!a.application?.gpa) { - return flipIfDescending(1, asc); + return flipIfDescending(-1, asc); } if (!b.application?.gpa) { - return flipIfDescending(-1, asc); + return flipIfDescending(1, asc); } if (a.application?.gpa === b.application?.gpa) { return 0; } - return a.application?.gpa > b.application?.gpa + return a.application?.gpa <= b.application?.gpa ? flipIfDescending(-1, asc) : flipIfDescending(1, asc); }); @@ -148,11 +148,11 @@ function sortByGpa(applicantSummaries: ApplicantSummary[], asc = true) { function sortByYip(applicantSummaries: ApplicantSummary[], asc = true) { applicantSummaries.sort((a, b) => { if (!a.application?.yip) { - return 1; + return flipIfDescending(-1, asc); } if (!b.application?.yip) { - return -1; + return flipIfDescending(1, asc); } if (a.application?.yip === b.application?.yip) { @@ -168,11 +168,15 @@ function sortByYip(applicantSummaries: ApplicantSummary[], asc = true) { function sortByDepartment(applicantSummaries: ApplicantSummary[], asc = true) { applicantSummaries.sort((a, b) => { if (!a.application?.department) { - return flipIfDescending(1, asc); + return flipIfDescending(-1, asc); } if (!b.application?.department) { - return flipIfDescending(-1, asc); + return flipIfDescending(1, asc); + } + + if (a.application?.department === b.application?.department) { + return 0; } return a.application?.department < b.application?.department @@ -265,11 +269,11 @@ function sortByTotalHoursAssigned( ) { applicantSummaries.sort((a, b) => { if (!a.matches) { - return 1; + return flipIfDescending(-1, asc); } if (!b.matches) { - return -1; + return flipIfDescending(1, asc); } const aHours = a.hoursAssigned; @@ -308,23 +312,22 @@ function sortByRemainingHoursOwed( asc = true ) { applicantSummaries.sort((a, b) => { - if (!a.matches || !a.guarantee) { - return 1; - } + const aHoursOwed = a.guarantee?.minHoursOwed || 0; + const bHoursOwed = a.guarantee?.minHoursOwed || 0; - if (!b.matches || !b.guarantee) { - return -1; + // Neither applicant has a guarantee to worry about + if (aHoursOwed === bHoursOwed) { + return 0; } let aHoursRemaining = - (a.guarantee?.minHoursOwed || 0) - + aHoursOwed - (a.guarantee?.previousHoursFulfilled || 0) - - a.hoursAssigned; - + (a.hoursAssigned || 0); let bHoursRemaining = - (b.guarantee?.minHoursOwed || 0) - + bHoursOwed - (b.guarantee?.previousHoursFulfilled || 0) - - b.hoursAssigned; + (b.hoursAssigned || 0); if (aHoursRemaining === bHoursRemaining) { return 0; diff --git a/frontend/src/views/admin/matching/styles.css b/frontend/src/views/admin/matching/styles.css index 53d6b657..59b3df33 100644 --- a/frontend/src/views/admin/matching/styles.css +++ b/frontend/src/views/admin/matching/styles.css @@ -62,10 +62,14 @@ margin-bottom: 10px; } -.position-search > form { +.position-search > .form-inline { flex-grow: 1; } +.position-search > .form-inline > input { + width: 100%; +} + .filter-button-container { padding-right: 10px; margin: 5px; @@ -91,9 +95,33 @@ margin-right: 15px; margin-bottom: 5px; box-shadow: 3px 2px 5px -3px #7d7d7d; + transition: height 0.4s ease-out; +} + +.position-row.selected { + height: 65px; + border: 2px; + border-style: dashed dashed dashed none; +} + +.position-row.selected.empty { + border-color: #d70000; +} + +.position-row.selected.under { + border-color: #ffcc16; +} + +.position-row.selected.matched { + border-color: #00c12b; } -.position-row:selected { +.position-row.selected.over { + border-color: #00e5f3; +} + +.position-row.selected > * >.position-row-info { + flex-wrap: wrap; } .position-row-body { @@ -119,6 +147,15 @@ z-index: 10; } +.position-row-detail { + flex-basis: 100%; + padding-left: 10px; + padding-right: 10px; + font-weight: 250; + font-size: smaller; + margin-top: 2px; +} + .position-row-background > .progress { height: 100%; width: 100%; @@ -128,15 +165,15 @@ } .progress.over { - background-color: rgba(0, 229, 243, 0.15); + background-color: rgba(0, 229, 243, 0.25); } .progress.matched { - background-color: rgba(0, 193, 43, 0.15); + background-color: rgba(0, 193, 43, 0.25); } .progress.under { - background-color: rgba(255, 204, 22, 0.15); + background-color: rgba(255, 204, 22, 0.25); } .matching-course-main { @@ -162,7 +199,6 @@ width: 5px; height: 100%; background-color: #d5d5d5; - transition: width 0.5s ease, background-color 0.75s ease; } .status-sidebar.empty { @@ -181,10 +217,6 @@ background-color: #00e5f3; } -.status-sidebar.selected { - width: 25px; -} - .position-row-info > .position-title { flex-grow: 1; padding-left: 10px; @@ -311,10 +343,13 @@ .matching-course-body { padding: 0 3px; + height: 100%; + display: flex; + flex-direction: column; } -.matching-course-body > .table-responsive { - font-size: smaller; +.matching-course-body > .filter-table-container { + height: 100%; } .applicant-status-divider { @@ -367,22 +402,6 @@ font-size: smaller; } -.filter-modal { - height: 60%; - max-width: 65%; -} - -.filter-modal > .modal-content { - height: 100%; - width: 100%; -} - -.filter-modal > .modal-content > .modal-body { - overflow-y: scroll; - width: 100%; - height: 100%; -} - .filter-form { display: flex; flex-wrap: wrap; @@ -445,6 +464,10 @@ cursor: pointer; } +.applicant-view-filler { + flex-grow: 1; +} + .applicant-count { margin: 5px 0; font-size: smaller;