diff --git a/packages/common/src/ResourceSchedule.ts b/packages/common/src/ResourceSchedule.ts index deaa172e2..2bbbbb4a4 100644 --- a/packages/common/src/ResourceSchedule.ts +++ b/packages/common/src/ResourceSchedule.ts @@ -1,5 +1,6 @@ import { RRule, RRuleSet } from "rrule"; import { + TResource, TResourceScheduleData, TScheduleItemOpenClose, TTimezoneName, @@ -199,6 +200,27 @@ export default class ResourceSchedule { ); } + /** + * Returns bool to indicate if a resource is currently open. + * @param at The date to compare against. Defaults to `new Date()`. + */ + isOpen(at: Date = new Date()): boolean | null { + if (this.alwaysOpen) { + return true; + } + + const nextScheduleItemPeriod = this.getNextScheduleItemPeriod(at); + if (!nextScheduleItemPeriod) { + return null; + } + + if (at.getTime() > nextScheduleItemPeriod.open.getTime()) { + return true; + } + + return false; + } + /** * Converts data in the form of `TResourceScheduleData` (the format a resource schedule is sent over * the wire and stored in the database) into a full-fledged `ResourceSchedule` instance. diff --git a/packages/server/src/routes/api/category/[stub].ts b/packages/server/src/routes/api/category/[stub].ts index 4db6d53dd..72665299b 100644 --- a/packages/server/src/routes/api/category/[stub].ts +++ b/packages/server/src/routes/api/category/[stub].ts @@ -6,9 +6,9 @@ export async function get(req, res, _next) { const categoryDocument = await Category.getByStub(stub); if (categoryDocument) { - res - .status(200) - .json({ category: await categoryDocumentToCategory(categoryDocument) }); + res.status(200).json({ + category: await categoryDocumentToCategory(categoryDocument), + }); } else { res.status(404).json({ message: `Category ${stub} not found` }); } diff --git a/packages/web/src/components/CategoryResults.tsx b/packages/web/src/components/CategoryResults.tsx index 67b9c558a..3b8719349 100644 --- a/packages/web/src/components/CategoryResults.tsx +++ b/packages/web/src/components/CategoryResults.tsx @@ -5,6 +5,7 @@ import React from "react"; import ResourceList from "./ResourceList"; import SubCategories from "./SubCategories"; import Typography from "@material-ui/core/Typography"; +import { sortByOpen } from "../utils/schedule"; import { useParams } from "react-router-dom"; import useResourcesByCategory from "./useResourcesByCategory"; import useResourcesBySubcategory from "./useResourcesBySubcategory"; @@ -44,7 +45,9 @@ const CategoryResults = ({ const status = subcategoryStub ? subcategoryResourcesStatus : categoryResourcesStatus; - const resources = subcategoryStub ? subcategoryResources : categoryResources; + const resources = sortByOpen( + subcategoryStub ? subcategoryResources : categoryResources + ); return ( <> diff --git a/packages/web/src/components/ResourceCard.tsx b/packages/web/src/components/ResourceCard.tsx index 6fab1ff92..3bf5614ec 100644 --- a/packages/web/src/components/ResourceCard.tsx +++ b/packages/web/src/components/ResourceCard.tsx @@ -1,5 +1,3 @@ -import { getIsOpen, getNextOpenText } from "../utils/schedule"; - import Card from "@material-ui/core/Card"; import CardActionArea from "@material-ui/core/CardActionArea"; import CardActions from "@material-ui/core/CardActions"; @@ -13,6 +11,8 @@ import ScheduleIcon from "@material-ui/icons/Schedule"; import { TResource } from "@upswyng/types"; import { Theme } from "@material-ui/core/styles/createMuiTheme"; import Typography from "@material-ui/core/Typography"; +import { getNextOpenText } from "../utils/schedule"; + import makeStyles from "@material-ui/styles/makeStyles"; import { useHistory } from "react-router-dom"; @@ -63,7 +63,7 @@ const ResourceCard = ({ index = 1, placeholder, resource }: Props) => { const { name, resourceId, schedule, streetViewImage } = resource; const parsedSchedule = ResourceSchedule.parse(schedule); - const isOpen = getIsOpen(parsedSchedule); + const isOpen = parsedSchedule.isOpen(); const scheduleText = getNextOpenText(parsedSchedule); const classes = useStyles({ index, diff --git a/packages/web/src/utils/schedule.ts b/packages/web/src/utils/schedule.ts index 42e5af073..a7607db80 100644 --- a/packages/web/src/utils/schedule.ts +++ b/packages/web/src/utils/schedule.ts @@ -1,5 +1,6 @@ import { TDay, + TResource, TSchedule, TScheduleItemOpenClose, TSchedulePeriod, @@ -88,24 +89,6 @@ const getOpensAtText = ({ open }: TScheduleItemOpenClose) => sameElse: "MMM Do", }); -export const getIsOpen = (schedule: ResourceSchedule): boolean | null => { - if (schedule.alwaysOpen) { - return true; - } - - const currentDt = new Date(); - const nextScheduleItemPeriod = schedule.getNextScheduleItemPeriod(currentDt); - if (!nextScheduleItemPeriod) { - return null; - } - - if (currentDt.getTime() > nextScheduleItemPeriod.open.getTime()) { - return true; - } - - return false; -}; - export const getNextOpenText = (schedule: ResourceSchedule): string => { if (schedule.alwaysOpen) { return "Open 24/7"; @@ -123,3 +106,15 @@ export const getNextOpenText = (schedule: ResourceSchedule): string => { return `open ${getOpensAtText(nextScheduleItemPeriod)}`; }; + +export const sortByOpen = (resources: TResource[] | undefined) => { + return resources + ? resources.sort((resourceA, resourceB) => { + const A = ResourceSchedule.parse(resourceA.schedule).isOpen(); + const B = ResourceSchedule.parse(resourceB.schedule).isOpen(); + if (A && !B) return -1; + if (B && !A) return 1; + return 0; + }) + : []; +};