+
+
diff --git a/resq/frontend/src/components/MapIcons.js b/resq/frontend/src/components/MapIcons.js
new file mode 100644
index 00000000..8016c36b
--- /dev/null
+++ b/resq/frontend/src/components/MapIcons.js
@@ -0,0 +1,28 @@
+import {Cancel, LocalFireDepartment, LocalHospital} from "@mui/icons-material";
+import * as React from "react";
+
+export const AnnotationIcon = ({icon, color}) =>
+ ({
+ fire:
,
+ health:
,
+ closed:
+ })[icon]
+export const MarkerIcon = ({color}) => (
+
+);
\ No newline at end of file
diff --git a/resq/frontend/src/pages/ListCards.js b/resq/frontend/src/pages/ListCards.js
new file mode 100644
index 00000000..1636f947
--- /dev/null
+++ b/resq/frontend/src/pages/ListCards.js
@@ -0,0 +1,209 @@
+import * as React from "react";
+import {useEffect, useState} from "react";
+import axios from "axios";
+import {Card, CardActions, CardContent, CardHeader, Collapse, IconButton} from "@mui/material";
+import Avatar from "@mui/material/Avatar";
+import {type_colors} from "../Colors";
+import Typography from "@mui/material/Typography";
+import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
+import styled from "styled-components";
+import {AnnotationIcon} from "../components/MapIcons";
+
+const ExpandMore = styled(IconButton)`
+ transform: ${({expand}) => !expand ? 'rotate(0deg)' : 'rotate(180deg)'};
+ margin-left: auto;
+ transition: transform 150ms;
+`
+const OffsetActions = styled(CardActions)`
+ transform: translate(-8px, -28px);
+ height: 0;
+ padding: 0;
+ flex-direction: row-reverse;
+`
+
+async function getAddress(latitude, longitude) {
+ try {
+ const response = await axios.get(
+ `https://maps.googleapis.com/maps/api/geocode/json?latlng=${latitude},${longitude}&key=AIzaSyCehlfJwJ-V_xOWZ9JK3s0rcjkV2ga0DVg`
+ );
+
+ return response.data.results[0]?.formatted_address || 'Unknown';
+ } catch (error) {
+ console.error('Error fetching location name:', error);
+ }
+}
+
+
+export const AnnotationCard = ({item: {title, short_description, long_description, latitude, longitude, category}}) => {
+ const [expanded, setExpanded] = useState(false);
+ const [locationName, setLocationName] = useState('');
+
+ useEffect(() => {
+ (async () => setLocationName(await getAddress(latitude, longitude)))();
+ }, [latitude, longitude]);
+
+ return
+
+
+
+ }
+ titleTypographyProps={{variant: 'h6'}}
+ title={title}
+ />
+
+
+ {short_description}
+
+
+ Location: {`${locationName}`}
+
+
+
+ {/*
+
+
+
+
+ */}
+ setExpanded(!expanded)}
+ aria-expanded={expanded}
+ aria-label="show more"
+ >
+
+
+
+
+
+ {long_description}
+
+
+ ;
+}
+
+export const RequestCard = ({item: {requester, urgency, needs, status, longitude, latitude}}) => {
+ const [expanded, setExpanded] = useState(false);
+ const [locationName, setLocationName] = useState('');
+
+ useEffect(() => {
+ (async () => setLocationName(await getAddress(latitude, longitude)))();
+ }, [latitude, longitude]);
+
+ return
+
+ Rq
+
+ }
+ titleTypographyProps={{variant: 'h6'}}
+ title={needs.map(({name, quantity}) => `${quantity} ${name}`).join(", ")}
+ />
+
+
+ Urgency: {urgency} | Status: {status}
+
+
+
+ Made by: {requester.name} {requester.surname}
+
+
+ Location: {`${locationName}`}
+
+
+
+ {/*
+
+
+
+
+ */}
+ setExpanded(!expanded)}
+ aria-expanded={expanded}
+ aria-label="show more"
+ >
+
+
+
+
+
+ {needs.map(({name, description, quantity}) =>
+
+ {quantity} {name}: {description}
+
+ )}
+
+
+ ;
+}
+
+
+export const ResourceCard = ({item: {owner, urgency, resources, status, longitude, latitude}}) => {
+ const [expanded, setExpanded] = useState(false);
+ const [locationName, setLocationName] = useState('');
+
+ useEffect(() => {
+ (async () => setLocationName(await getAddress(latitude, longitude)))();
+ }, [latitude, longitude]);
+
+
+ return
+
+ Rs
+
+ }
+ titleTypographyProps={{variant: 'h6'}}
+ title={resources.map(({name, quantity}) => `${quantity} ${name}`).join(", ")}
+ />
+
+
+ Urgency: {urgency} | Status: {status}
+
+
+ Owner: {owner.name} {owner.surname}
+
+
+ Location: {`${locationName}`}
+
+
+
+ {/*
+
+
+
+
+ */}
+ setExpanded(!expanded)}
+ aria-expanded={expanded}
+ aria-label="show more"
+ >
+
+
+
+
+
+ {resources.map(({name, description, quantity}) =>
+
+ {quantity} {name}: {description}
+
+ )}
+
+
+ ;
+}
+export const cards = {
+ "Resource": ResourceCard,
+ "Request": RequestCard,
+ "Annotation": AnnotationCard
+}
\ No newline at end of file
diff --git a/resq/frontend/src/pages/MapDemo.js b/resq/frontend/src/pages/MapDemo.js
index e86a6ca9..6680724e 100644
--- a/resq/frontend/src/pages/MapDemo.js
+++ b/resq/frontend/src/pages/MapDemo.js
@@ -1,30 +1,12 @@
import * as React from 'react';
-import Avatar from '@mui/material/Avatar';
-import Button from '@mui/material/Button';
+import {useEffect, useState} from 'react';
import CssBaseline from '@mui/material/CssBaseline';
-import TextField from '@mui/material/TextField';
-import FormControlLabel from '@mui/material/FormControlLabel';
-import Checkbox from '@mui/material/Checkbox';
-import Link from '@mui/material/Link';
import Box from '@mui/material/Box';
-import Grid from '@mui/material/Grid';
-import Typography from '@mui/material/Typography';
-import { createTheme, ThemeProvider } from '@mui/material/styles';
-import disasterImage from '../disaster.png';
+import {createTheme, ThemeProvider} from '@mui/material/styles';
import Container from '@mui/material/Container';
-import { useNavigate } from 'react-router-dom';
import DisasterMap from "../components/DisasterMap";
-import { useState, useEffect } from 'react';
-import { Card, CardActions, CardContent, CardHeader, Collapse, IconButton } from "@mui/material";
-import { type_colors } from "../Colors";
-import FavoriteIcon from '@mui/icons-material/Favorite';
-import ShareIcon from '@mui/icons-material/Share';
-import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
-import MoreVertIcon from '@mui/icons-material/MoreVert';
-import styled from "styled-components";
-import axios from 'axios';
-import Geocode from 'react-geocode';
-import MapDataGrid from '../components/MapDataGrid'
+import {cards} from "./ListCards";
+import {MultiCheckbox} from "./MultiCheckbox";
const customTheme = createTheme({
@@ -36,171 +18,20 @@ const customTheme = createTheme({
});
-const ExpandMore = styled(IconButton)`
- transform: ${({ expand }) => !expand ? 'rotate(0deg)' : 'rotate(180deg)'};
- margin-left: auto;
- transition: transform 150ms;
-`
-
-const OffsetActions = styled(CardActions)`
- transform: translate(-8px, -28px);
- height: 0;
- padding: 0;
-`
-
-const RequestCard = ({ request: { requester, urgency, needs, status, longitude, latitude } }) => {
- const [expanded, setExpanded] = useState(false);
- const [locationName, setLocationName] = useState('');
- const [cityName, setCityName] = useState('');
-
- useEffect(() => {
- const reverseGeocode = async () => {
- try {
- const response = await axios.get(
- `https://maps.googleapis.com/maps/api/geocode/json?latlng=${latitude},${longitude}&key=AIzaSyCehlfJwJ-V_xOWZ9JK3s0rcjkV2ga0DVg`
- );
-
- const cityName = response.data.results[0]?.formatted_address || 'Unknown';
- setLocationName(cityName);
- setCityName(cityName);
- } catch (error) {
- console.error('Error fetching location name:', error);
- }
- };
-
- reverseGeocode();
- }, [latitude, longitude]);
-
- return
-
- Rq
-
- }
- titleTypographyProps={{ variant: 'h6' }}
- title={needs.map(({ name, quantity }) => `${quantity} ${name}`).join(", ")}
- />
-
-
- Urgency: {urgency} | Status: {status}
-
-
-
- Made by: {requester.name} {requester.surname}
-
-
- Location: {`${cityName}`}
-
-
-
- {/*
-
-
-
-
- */}
- setExpanded(!expanded)}
- aria-expanded={expanded}
- aria-label="show more"
- >
-
-
-
-
-
- {needs.map(({ name, description, quantity }) =>
-
- {quantity} {name}: {description}
-
- )}
-
-
- ;
-}
-
-
-const ResourceCard = ({ request: { owner, urgency, resources, status, longitude, latitude } }) => {
- const [expanded, setExpanded] = useState(false);
- const [locationName, setLocationName] = useState('');
- const [cityName, setCityName] = useState('');
-
- useEffect(() => {
- const reverseGeocode = async () => {
- try {
- const response = await axios.get(
- `https://maps.googleapis.com/maps/api/geocode/json?latlng=${latitude},${longitude}&key=AIzaSyCehlfJwJ-V_xOWZ9JK3s0rcjkV2ga0DVg`
- );
-
- const cityName = response.data.results[0]?.formatted_address || 'Unknown';
- setLocationName(cityName);
- setCityName(cityName);
- } catch (error) {
- console.error('Error fetching location name:', error);
- }
- };
-
- reverseGeocode();
- }, [latitude, longitude]);
-
-
- return
-
- Rs
-
- }
- titleTypographyProps={{ variant: 'h6' }}
- title={resources.map(({ name, quantity }) => `${quantity} ${name}`).join(", ")}
- />
-
-
- Urgency: {urgency} | Status: {status}
-
-
- Owner: {owner.name} {owner.surname}
-
-
- Location: {`${cityName}`}
-
-
-
- {/*
-
-
-
-
- */}
- setExpanded(!expanded)}
- aria-expanded={expanded}
- aria-label="show more"
- >
-
-
-
-
-
- {resources.map(({ name, description, quantity }) =>
-
- {quantity} {name}: {description}
-
- )}
-
-
- ;
-}
-
-const cards = {
- "Resource": ResourceCard,
- "Request": RequestCard
-}
-
const mock_markers = [
+ {
+ type: "Annotation",
+ latitude: 41.089,
+ longitude: 29.053,
+ category: "health",
+ title: "First Aid Clinic",
+ short_description: "First aid clinic and emergency wound care. Open 24 hours.",
+ long_description: "Welcome to our First Aid Clinic, a dedicated facility committed to providing immediate and " +
+ "compassionate healthcare 24 hours a day. Our experienced team of healthcare professionals specializes in " +
+ "emergency wound care and first aid assistance, ensuring you receive prompt attention when you need it most. " +
+ "From minor cuts to more serious injuries, our clinic is equipped to handle a range of medical concerns, " +
+ "promoting healing and preventing complications."
+ },
{
type: "Request",
latitude: 37.08,
@@ -236,6 +67,7 @@ const mock_markers = [
needs: [
{
name: "Power banks",
+ category: "Other",
description: "Power banks for the staff, preferably with cables included.",
quantity: 30
},
@@ -254,39 +86,117 @@ const mock_markers = [
resources: [
{
name: "Bottled Water",
+ category: "Water",
description: "1.5 liters bottles",
quantity: 300,
},
+ {
+ name: "Canned Beans",
+ category: "Food",
+ description: "400 gram cans",
+ quantity: 500,
+ },
],
status: "READY"
},
]
+function getAllCategories(item) {
+ switch (item.type) {
+ case "Annotation":
+ return [item?.category]
+ case "Resource":
+ return item.resources.map(resource => resource?.category)
+ case "Request":
+ return item.needs.map(need => need?.category)
+ default:
+ return []
+ }
+}
-export default function MapDemo() {
+const makeFilterByCategory = categories => {
+ if (categories.length === 0)
+ return () => true
+ return item => {
+ switch (item.type) {
+ case "Annotation":
+ return categories.indexOf(item?.category) !== -1
+ case "Resource":
+ return !item.resources.every(resource => categories.indexOf(resource?.category) === -1)
+ case "Request":
+ return !item.needs.every(need => categories.indexOf(need?.category) === -1)
+ default:
+ return false
+ }
+ }
+};
+
+const makeFilterByType = (typeFilter) => item => typeFilter.length === 0 || typeFilter.indexOf(item.type) !== -1
- const navigate = useNavigate();
+export default function MapDemo() {
+ const [allMarkers, setAllMarkers] = useState(mock_markers)
+ const [shownMarkers, setShownMarkers] = useState(allMarkers)
const [selectedPoint, setSelectedPoint] = useState(null)
- const SelectedCard = selectedPoint && cards[selectedPoint.type]
+
+ const [typeFilter, setTypeFilter] = useState([])
+ const [timeFilter, setTimeFilter] = useState([])
+ const [amountFilter, setAmountFilter] = useState([])
+ const [categoryFilter, setCategoryFilter] = useState([])
+ const [mapBounds, setMapBounds] = useState([])
+
+ useEffect(() => setShownMarkers(
+ allMarkers
+ .filter(makeFilterByCategory(categoryFilter))
+ .filter(makeFilterByType(typeFilter))
+ ), [allMarkers, categoryFilter, typeFilter])
+
// noinspection JSValidateTypes
return (
-
-
-
- {selectedPoint && <>
-
-
-
-
- >
- }
- {(!selectedPoint) && <>
-
-
- >}
-
-
+
+
+
+
+ v && array.indexOf(v) === i)}
+ onChosenChanged={setCategoryFilter}/>
+
+
+
+
+
+ {shownMarkers.map((marker) => {
+ const SelectedCard = cards[marker.type]
+ return < SelectedCard item={marker} onClick={() => setSelectedPoint(marker)}/>
+ })}
+
+
+
+
diff --git a/resq/frontend/src/pages/MultiCheckbox.js b/resq/frontend/src/pages/MultiCheckbox.js
new file mode 100644
index 00000000..eab5b426
--- /dev/null
+++ b/resq/frontend/src/pages/MultiCheckbox.js
@@ -0,0 +1,78 @@
+import {Chip, FormControl, InputLabel, MenuItem, OutlinedInput, Select, useTheme} from "@mui/material";
+import * as React from "react";
+import {useEffect, useId} from "react";
+import Box from "@mui/material/Box";
+
+function getDropDownStyles(name, personName, theme) {
+ return {
+ fontWeight:
+ personName.indexOf(name) === -1
+ ? theme.typography.fontWeightRegular
+ : theme.typography.fontWeightMedium,
+ };
+}
+
+export const MultiCheckbox = ({name, choices, onChosenChanged}) => {
+ const theme = useTheme();
+
+ const ITEM_HEIGHT = 48;
+ const ITEM_PADDING_TOP = 8;
+ const MenuProps = {
+ PaperProps: {
+ style: {
+ maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
+ width: 250,
+ },
+ },
+ };
+
+ const [currentChoices, setCurrentChoices] = React.useState([]);
+ const label_id = useId();
+ const select_id = useId();
+ const input_id = useId();
+
+
+ const handleChange = (event) => {
+ const {
+ target: {value},
+ } = event;
+ setCurrentChoices(
+ typeof value === 'string' ? value.split(',') : value,
+ );
+ };
+
+ useEffect(() => {
+ onChosenChanged && onChosenChanged(currentChoices)
+ }, [onChosenChanged, currentChoices])
+
+ return
+ {name}
+
+
+}
\ No newline at end of file