Skip to content

Commit

Permalink
Merge pull request #222 from bettersg/feature/210-route-planning
Browse files Browse the repository at this point in the history
Feature/210 route planning
  • Loading branch information
neozhixuan authored Dec 22, 2023
2 parents 47069d5 + 09abc54 commit 0422ea6
Show file tree
Hide file tree
Showing 14 changed files with 677 additions and 59 deletions.
6 changes: 6 additions & 0 deletions client/app-context/UserSelectionContext/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ export type TResult = { facilities: FacilityType[] };
export type RecyclingLocationResults = {
results: Record<string, TResult>;
facilitiesList: number[];
route: {
path: number[];
distanceBtwFacilities: number[];
coords: [number, number][];
complete: boolean;
};
};

export interface UserSelectionContextState {
Expand Down
8 changes: 8 additions & 0 deletions client/components/map/CustomPolyline.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Polyline } from "react-leaflet";

export const CustomPolyline: React.FC<{
lineCoords: [number, number][];
color: string;
}> = ({ lineCoords, color }) => {
return <Polyline positions={lineCoords} color={color} />;
};
22 changes: 17 additions & 5 deletions client/components/map/FacilityCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import Link from "next/link";
import { useSheetyData } from "hooks/useSheetyData";
import { CheckIcon } from "@chakra-ui/icons";
import { categoriesProcessor } from "./utils";
import { Methods } from "api/sheety/enums";
export const FacilityCard = ({
items,
facCardDetails,
Expand All @@ -28,15 +29,26 @@ export const FacilityCard = ({
};

const itemsAccepted = items.filter((item) => {
return facCardDetails.categoriesAccepted.includes(getItemCategory(item.name));
const itemMethod = item.method as Methods;
// For an item to be accepted: (1) Same method (2) Same category
if (facCardDetails.methodsAccepted.includes(itemMethod)) {
return facCardDetails.categoriesAccepted.includes(getItemCategory(item.name));
}
});

const itemsNotAccepted = items.filter((item) => {
const itemMethod = item.method as Methods;
// For an item to be rejected: Anything of a different Method than the facility.
if (!facCardDetails.methodsAccepted.includes(itemMethod)) {
return true;
}
// EDGE CASE: if its a different Method but the facility still takes this item.
return !facCardDetails.categoriesAccepted.includes(getItemCategory(item.name));
});

return (
<Flex
h={"470px"}
bg="white"
paddingBottom={2}
position={"fixed"}
Expand All @@ -53,14 +65,14 @@ export const FacilityCard = ({
{!isExpanded && (
<Box
position={"fixed"}
height={"10%"}
height={"15%"}
width={"100%"}
bottom={"50%"}
bottom={"43%"}
zIndex={1001}
bgGradient={"linear(transparent 0%, white 60%)"}
/>
)}
<Button maxHeight={"2%"} padding={2.5} bg={"white"} onClick={handleMovement}>
<Button height={"5%"} padding={1} bg={"white"} onClick={handleMovement}>
{!isExpanded ? <ChevronUpIcon /> : <ChevronDownIcon />}
</Button>
<Flex
Expand Down Expand Up @@ -118,7 +130,7 @@ export const FacilityCard = ({
))}
</Flex>
<Text fontSize={"sm"} as={"b"}>
They also accept these items:
They generally accept items of these categories:
</Text>
<Flex gap={2} fontSize={"xs"} fontWeight={500} width={"100%"} wrap={"wrap"}>
{categoriesProcessor(facCardDetails.categoriesAccepted)}
Expand Down
2 changes: 1 addition & 1 deletion client/components/map/FilterPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ const ChipRadio = (props: PropsWithChildren<UseRadioProps>) => {
);
};

const Chip = ({
export const Chip = ({
value,
children,
isChecked,
Expand Down
190 changes: 190 additions & 0 deletions client/components/map/RouteCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
import { Flex, Box, Button, HStack, Text, VStack, Image } from "@chakra-ui/react";
import { ChevronDownIcon, ChevronUpIcon, ExternalLinkIcon } from "@chakra-ui/icons";
import { useState } from "react";
import styles from "components/home/hideScrollbar.module.css";
import { Chip } from "./FilterPanel";
import { useSheetyData } from "hooks/useSheetyData";
import { categoriesProcessor } from "./utils";
import { TItemSelection, TEmptyItem } from "app-context/SheetyContext/types";
import { Methods } from "api/sheety/enums";
import { RecyclingLocationResults } from "app-context/UserSelectionContext/types";
import Link from "next/link";
export const RouteCard = ({
items,
route,
results,
}: {
items: (TItemSelection | TEmptyItem)[];
route: RecyclingLocationResults["route"];
results: RecyclingLocationResults["results"];
}) => {
const { getFacility, getItemCategory } = useSheetyData();
const [isExpanded, setIsExpanded] = useState(false);
const [translateY, setTranslateY] = useState(50);
const handleMovement = () => {
isExpanded ? setTranslateY(50) : setTranslateY(0);
setIsExpanded(!isExpanded);
};

const calculateSum = (arr: number[]) => {
return arr.reduce((accumulator, value) => {
return accumulator + value;
}, 0);
};

return (
<Flex
h={"300px"}
bg="white"
paddingBottom={2}
position={"fixed"}
transform={`translate(-50%, ${translateY}%)`}
bottom={0}
left="50%"
width={{ base: "86%", sm: "50%", md: "40%", lg: "25%" }}
zIndex={1000}
rounded="xl"
flexDir={"column"}
transition={"transform 0.3s ease-in-out"}
>
{/* Gradient */}
{!isExpanded && (
<Box
position={"fixed"}
height={"10%"}
width={"100%"}
bottom={"50%"}
zIndex={1001}
bgGradient={"linear(transparent 0%, white 60%)"}
/>
)}
<Button height={"10%"} padding={1} bg={"white"} onClick={handleMovement}>
{!isExpanded ? <ChevronUpIcon /> : <ChevronDownIcon />}
</Button>
<Flex
flexDir={"column"}
paddingInline={3}
overflowY={"scroll"}
className={styles.hideScrollbar}
>
{/* Header */}
<HStack mb={2} justifyContent={"space-between"}>
<Text as="b">
{route.path.length - 1} stop
{route.path.length > 2 && "s"}
</Text>
<Text as="b">
Total {(calculateSum(route.distanceBtwFacilities) * 1000).toFixed(0)}m
</Text>
</HStack>
{/* Routes */}
{route.path.slice(1).map((facID: number, idx) => {
const facility = getFacility(facID);
const itemsAccepted = items.filter((item) => {
return (
facility.categoriesAccepted.includes(getItemCategory(item.name)) &&
facility.methodsAccepted.includes(item.method as Methods)
);
});
return (
<HStack key={facID} w={"100%"} mb={5}>
<Box height="full" w={"5%"} alignSelf={"start"}>
<Box w="full" h="full">
<Image
h="6%"
ml={0}
src={"/blueCircle.png"}
alt="blue circle"
/>
<Image
ml={1}
h="90%"
justifySelf={"center"}
src={"/blueLine.png"}
alt="blue line"
/>
</Box>
</Box>
<VStack alignSelf={"start"} w={"95%"} gap={1}>
<Flex
w={"full"}
flexDirection={"row"}
justifyContent={"space-between"}
mb={0}
>
<Text as="b" fontSize={"sm"} w={"70%"}>
{facility.channelName}
</Text>
<Flex gap={5} w={"30%"}>
<Text fontSize={"xs"} w={"50%"}>
{itemsAccepted.length} item
{itemsAccepted.length > 1 && "s"}
</Text>
<Text fontSize={"xs"} w={"50%"}>
{(route.distanceBtwFacilities[idx] * 1000).toFixed(0)}m
</Text>
</Flex>
</Flex>
<Text w={"full"} fontSize={"xs"}>
{facility.address}
</Text>
<Flex
mt={2}
flex={1}
gap={2}
flexWrap="wrap"
maxH="70px"
overflow="auto"
alignItems="center"
justifyContent={"start"}
w={"full"}
>
{itemsAccepted &&
itemsAccepted.map((item, idx) => (
<Chip isChecked={true} key={idx}>
{item.name}
</Chip>
))}
</Flex>
<Box w="full">
<Text fontSize={"xs"} as="b">
They also accept:
</Text>
<Text fontSize={"xs"}>
{categoriesProcessor(facility.categoriesAccepted)}
</Text>
<Box w={"full"} mt={5}>
{facility.website ? (
<Text fontSize={"xs"}>
Please check{" "}
<Link href={facility.website}>
<Text
as="span"
color="blue.500"
_hover={{ cursor: "pointer" }}
textDecoration={"underline"}
>
here
</Text>
</Link>{" "}
for more information.
</Text>
) : (
<Text fontSize={"xs"}>
No website available for this facility. :(
</Text>
)}
</Box>
</Box>
</VStack>
</HStack>
);
})}
<Button isDisabled={true} w="full" p={5} gap={2} bgColor={"teal"} color="white">
<ExternalLinkIcon />
<Text>Get Directions</Text>
</Button>
</Flex>
</Flex>
);
};
1 change: 1 addition & 0 deletions client/components/map/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from "./FacilityCard";
export * from "./RouteCard";
export * from "./FilterPanel";
export * from "./MapContextProvider";
export * from "./NearbyFacilitiesPanel";
Expand Down
2 changes: 0 additions & 2 deletions client/hooks/useLeafletWindow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ const useLeafletWindow = () => {
const [leafletWindow, setLeafletWindow] = useState(
typeof window === "undefined" ? undefined : window.L,
);
console.log("window is ", window.L);

useEffect(() => {
const interval = setInterval(() => {
if (window.L) {
Expand Down
Binary file added client/public/blueCircle.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added client/public/blueLine.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added client/public/locationButton.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 0422ea6

Please sign in to comment.