Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ryder PCA Sorting #540

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 19 additions & 32 deletions frontend/alert/components/AutoComplete.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { useOnClickOutside } from "pcx-shared-components/src/useOnClickOutside";
import { Input } from "./Input";
import { Section } from "../types";
import GroupSuggestion from "./GroupSuggestion";
import { groupByProperty } from "../util";

/* A function that takes in a search term and returns a promise with both the search term and
the search results.
Expand Down Expand Up @@ -137,18 +138,17 @@ interface TSuggestion {
}

interface AutoCompleteProps {
defaultValue: string,
setTimeline: React.Dispatch<React.SetStateAction<string | null>>,
selectedCourses: Set<Section>,
setSelectedCourses: React.Dispatch<React.SetStateAction<Set<Section>>>,
value: string,
setValue: React.Dispatch<React.SetStateAction<string>>,
inputRef: React.RefObject<HTMLInputElement>,
clearSelections: () => void,
clearInputValue: () => void,
defaultValue: string;
setTimeline: React.Dispatch<React.SetStateAction<string | null>>;
selectedCourses: Set<Section>;
setSelectedCourses: React.Dispatch<React.SetStateAction<Set<Section>>>;
value: string;
setValue: React.Dispatch<React.SetStateAction<string>>;
inputRef: React.RefObject<HTMLInputElement>;
clearSelections: () => void;
clearInputValue: () => void;
}


const AutoComplete = ({
defaultValue = "",
setTimeline,
Expand Down Expand Up @@ -187,13 +187,11 @@ const AutoComplete = ({
inputRef.current.value = generateCoursesValue();
} else if (selectedCourses.size === 1) {
// display the one selected section
setValue(
selectedCourses.values().next().value.section_id
);
setValue(selectedCourses.values().next().value.section_id);
inputRef.current.value = selectedCourses
.values()
.next().value.section_id;
}
}
}
}, [selectedCourses]);

Expand Down Expand Up @@ -247,24 +245,13 @@ const AutoComplete = ({
* Returns suggested suggestion grouped by course
* @return suggestions
*/
const groupedSuggestions = suggestions
.sort((a, b) => a.section_id.localeCompare(b.section_id))
.reduce((res, obj) => {
const [courseName, midNum, endNum] = obj.section_id.split("-");
const { activity } = obj;

if (res[`${courseName}-${midNum}`]) {
if (res[`${courseName}-${midNum}`][activity]) {
res[`${courseName}-${midNum}`][activity].push(obj);
} else {
res[`${courseName}-${midNum}`][activity] = [obj];
}
} else {
res[`${courseName}-${midNum}`] = { [activity]: [obj] };
}

return res;
}, {});
const groupedSuggestions = groupByProperty(
suggestions,
(a, b) => a.section_id.localeCompare(b.section_id),
"-",
(obj) => obj.section_id,
(obj) => obj.activity
);

return (
<Container
Expand Down
41 changes: 41 additions & 0 deletions frontend/alert/components/managealert/AlertHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React from "react";
import styled from "styled-components";
import {
GridItem,
GridCourseTitle,
} from "pcx-shared-components/src/common/layout";

const GridSubtitle = styled.div`
color: #282828;
font-size: 0.9rem;
padding-top: 0.4rem;
font-family: "Inter", sans-serif;
font-weight: bold;
white-space: nowrap;
`;

// Component for an alert entry (renders as a row in CSS grid)
interface AlertHeaderProps {
courseCode: string;
rowNum: number;
}
export const AlertHeader = ({ courseCode, rowNum }: AlertHeaderProps) => {
return (
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: courseCode and rowNum?

<>
<GridCourseTitle column={1} valign border>
<GridSubtitle>{courseCode.toUpperCase()}</GridSubtitle>
</GridCourseTitle>
{/* used to make sure grid line goes to end */}
{Array.from({ length: 7 }, (_, index) => (
<GridItem
column={index + 2}
row={rowNum}
border
halign
valign
talign
/>
))}
</>
);
};
25 changes: 12 additions & 13 deletions frontend/alert/components/managealert/AlertItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,14 @@ const StatusGridItem = styled(GridItem)`
const TrashImg = styled(Img)`
cursor: pointer;
opacity: 75%;

&:hover {
opacity: 100%;
}
&:active {
transform: translateY(0.1rem);
}

`
`;

// Component for an alert entry (renders as a row in CSS grid)
interface AlertItemProps {
Expand All @@ -40,7 +39,7 @@ interface AlertItemProps {
status: SectionStatus;
actions: AlertAction;
closed: AlertAction;
rownum: number;
rowNum: number;
checked: boolean;
toggleAlert: () => void;
alertHandler: () => void;
Expand All @@ -53,7 +52,7 @@ export const AlertItem = ({
status,
actions,
closed,
rownum,
rowNum,
checked,
toggleAlert,
alertHandler,
Expand All @@ -77,14 +76,14 @@ export const AlertItem = ({

return (
<>
<GridItem column={1} row={rownum} border halign valign>
<GridItem column={1} row={rowNum} border halign valign>
<input
type="checkbox"
checked={checked}
onChange={toggleAlert}
/>
</GridItem>
<GridItem column={2} row={rownum} border halign valign talign>
<GridItem column={2} row={rowNum} border halign valign talign>
{alertLastSent ? (
<P size="0.7rem">{alertLastSent}</P>
) : (
Expand All @@ -93,21 +92,21 @@ export const AlertItem = ({
</P>
)}
</GridItem>
<GridItem column={3} row={rownum} border halign valign talign>
<GridItem column={3} row={rowNum} border halign valign talign>
<P size="0.7rem">{course}</P>
</GridItem>
<StatusGridItem column={4} row={rownum} border halign valign>
<StatusGridItem column={4} row={rowNum} border halign valign>
<StatusInd background={statuscolor} />
<P size="0.7rem">{statustext}</P>
</StatusGridItem>
<GridItem border column={5} row={rownum} halign valign></GridItem>
<GridItem border column={6} row={rownum} halign valign>
<GridItem border column={5} row={rowNum} halign valign></GridItem>
<GridItem border column={6} row={rowNum} halign valign>
<ToggleSwitch type={actions} handleChange={alertHandler} />
</GridItem>
<GridItem border column={7} row={rownum} halign valign>
<GridItem border column={7} row={rowNum} halign valign>
<ToggleSwitch type={closed} handleChange={closedHandler} />
</GridItem>
<GridItem border column={8} row={rownum} halign valign>
<GridItem border column={8} row={rowNum} halign valign>
<TrashImg
src="/svg/trash.svg"
width="1.15rem"
Expand Down
79 changes: 56 additions & 23 deletions frontend/alert/components/managealert/ManageAlertUI.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import Header from "./Header";
import { AlertSearch } from "./AlertSearch";
import { AlertItem } from "./AlertItem";
import { maxWidth, PHONE } from "../../constants";
import { Alert, AlertAction, TAlertSel } from "../../types";
import { Alert, AlertAction, SectionStatus, TAlertSel } from "../../types";
import { AlertHeader } from "./AlertHeader";
import { groupByProperty } from "../../util";

const Container = styled.div`
background: #ffffff;
Expand Down Expand Up @@ -90,6 +92,8 @@ export const ManageAlert = ({
const [searchTimeout, setSearchTimeout] = useState<number>();
const [numSelected, setNumSelected] = useState(0);

let rowNum = 0;

useEffect(() => {
setNumSelected(
Object.values(alertSel).reduce((acc, x) => acc + (x ? 1 : 0), 0)
Expand All @@ -109,6 +113,17 @@ export const ManageAlert = ({
);
};

/**
* Returns alerts grouped by course
* @return grouped alerts
*/
const groupedAlerts = groupByProperty(
alerts,
(a, b) => a.section.localeCompare(b.section),
"-",
(obj) => obj.section
);

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we generalize this with the other grouping and put it in a shared util file?

return (
<Container>
<Flex margin="0.2rem 2rem 0.1rem 2rem" center valign spaceBetween>
Expand All @@ -123,28 +138,46 @@ export const ManageAlert = ({
batchSelectHandler={batchSelectHandler}
/>
<AlertGrid>
{alerts?.map?.((alert, i) => (
<AlertItem
key={alert.id}
checked={alertSel[alert.id]}
rownum={i + 1}
alertLastSent={alert.alertLastSent}
course={alert.section}
status={alert.status}
actions={alert.actions}
closed={alert.closedNotif}
toggleAlert={toggleAlert(alert.id)}
alertHandler={() =>
actionHandler(alert.id, alert.actions)
}
closedHandler={() =>
actionHandler(alert.id, alert.closedNotif)
}
deleteHandler={() =>
actionHandler(alert.id, AlertAction.DELETE)
}
/>
))}
{Object.keys(groupedAlerts).map((key) => {
return (
<>
<AlertHeader courseCode={key} rowNum={++rowNum} />
{groupedAlerts[key]?.map?.((alert) => {
return (
<AlertItem
key={alert.id}
checked={alertSel[alert.id]}
rowNum={++rowNum}
alertLastSent={alert.alertLastSent}
course={alert.section}
status={alert.status}
actions={alert.actions}
closed={alert.closedNotif}
toggleAlert={toggleAlert(alert.id)}
alertHandler={() =>
actionHandler(
alert.id,
alert.actions
)
}
closedHandler={() =>
actionHandler(
alert.id,
alert.closedNotif
)
}
deleteHandler={() =>
actionHandler(
alert.id,
AlertAction.DELETE
)
}
/>
);
})}
</>
);
})}
</AlertGrid>
</Container>
);
Expand Down
32 changes: 32 additions & 0 deletions frontend/alert/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,35 @@ export const mapActivityToString = (activity) => {

return activity in activityStringMap ? activityStringMap[activity] : "";
};

export function groupByProperty(
array,
sortingFn,
splitPattern,
keyExtractor,
activityExtractor = (obj) => undefined
) {
return array.sort(sortingFn).reduce((res, obj) => {
const key = keyExtractor(obj);
const activity = activityExtractor(obj);
const [courseName, midNum, endNum] = key.split(splitPattern);
if (activity) {
if (res[`${courseName}-${midNum}`]) {
if (res[`${courseName}-${midNum}`][activity]) {
res[`${courseName}-${midNum}`][activity].push(obj);
} else {
res[`${courseName}-${midNum}`][activity] = [obj];
}
} else {
res[`${courseName}-${midNum}`] = { [activity]: [obj] };
}
} else {
if (res[`${courseName}-${midNum}`]) {
res[`${courseName}-${midNum}`].push(obj);
} else {
res[`${courseName}-${midNum}`] = [obj];
}
}
return res;
}, {});
}
23 changes: 23 additions & 0 deletions frontend/shared-components/src/common/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// import React from "react";
import { string } from "prop-types";
import styled from "styled-components";

export const Container = styled.div`
Expand Down Expand Up @@ -47,6 +48,16 @@ export interface GridItemProps {
border?: boolean;
}

export interface GridCourseTitleProps {
valign?: boolean;
halign?: boolean;
talign?: boolean;
paddingLeft?: string;
color?: string;
border?: boolean;
column: number | string;
}

export const GridItem = styled.div<GridItemProps>`
display: flex;
align-items: ${(props) => (props.valign ? "center" : null)};
Expand All @@ -58,6 +69,18 @@ export const GridItem = styled.div<GridItemProps>`
border-bottom: ${(props) => (props.border ? "1px solid #ececec" : null)};
`;

export const GridCourseTitle = styled.div<GridCourseTitleProps>`
display: flex;
align-items: ${(props) => (props.valign ? "center" : null)};
justify-content: ${(props) => (props.halign ? "center" : null)};
text-align: ${(props) => (props.talign ? "center" : null)};
padding-left: ${(props) =>
props.paddingLeft ? props.paddingLeft : "2rem"};
background-color: ${(props) => (props.color ? props.color : "white")};
border-bottom: ${(props) => (props.border ? "1px solid #ececec" : null)};
grid-column: ${(props) => props.column};
`;

export const RightItem = styled.div`
height: 100%;
display: flex;
Expand Down
Loading