From 6e6640a85a872e6a67e0553c2160df1bc633bc07 Mon Sep 17 00:00:00 2001 From: Mahir Labib Dihan Date: Thu, 8 Aug 2024 03:11:24 +0600 Subject: [PATCH 001/253] Commit Before Major Change --- src/app/home/evaluation/page.js | 2 ++ src/components/Cards/QueryCard.jsx | 4 ++-- .../Containers/QuestionsContainer.jsx | 3 ++- src/components/Steppers/ContextStepper.jsx | 21 ++++++++++--------- 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/app/home/evaluation/page.js b/src/app/home/evaluation/page.js index 5e3317e..a83c1e4 100644 --- a/src/app/home/evaluation/page.js +++ b/src/app/home/evaluation/page.js @@ -35,6 +35,7 @@ import { showError, showSuccess } from "@/contexts/ToastProvider"; import EditIcon from "@mui/icons-material/Edit"; import Confirmation from "@/components/Dialogs/Confirmation"; import geminiApi from "@/api/geminiApi"; +import { getUserName } from "@/api/base"; const llmApis = { gpt4: { @@ -167,6 +168,7 @@ export default function LiveEvaluation() { }, evaluation: Object.entries(llmResults).map(([llm, result]) => ({ model_id: llmApis[llm].id, + model: llmApis[llm].name, answer: result.answer, verdict: result.verdict, })), diff --git a/src/components/Cards/QueryCard.jsx b/src/components/Cards/QueryCard.jsx index 6d2aa19..46b4f10 100644 --- a/src/components/Cards/QueryCard.jsx +++ b/src/components/Cards/QueryCard.jsx @@ -34,11 +34,11 @@ import categories from "@/database/categories.json"; import { convertFromSnake } from "@/services/utils"; import { AppContext } from "@/contexts/AppContext"; -export default function QueryCard({ entry, onEdit, isPersonal, mode }) { +export default function QueryCard({ entry, onEdit, isPersonal, mode, index }) { const [flag, setFlag] = useState(false); const { setQueries } = useContext(AppContext); const { isAuthenticated } = useAuth(); - const [expanded, setExpanded] = useState(false); + const [expanded, setExpanded] = useState(index === 0); const [category, setCategory] = useState(entry.classification); const handleDelete = async () => { diff --git a/src/components/Containers/QuestionsContainer.jsx b/src/components/Containers/QuestionsContainer.jsx index d5c7665..cfe60b6 100644 --- a/src/components/Containers/QuestionsContainer.jsx +++ b/src/components/Containers/QuestionsContainer.jsx @@ -120,12 +120,13 @@ export default function QuestionsContainer({ title, isPersonal, onEdit }) { on your own machine.
- {data.map((entry) => ( + {data.map((entry, index) => ( ))}
diff --git a/src/components/Steppers/ContextStepper.jsx b/src/components/Steppers/ContextStepper.jsx index dce64e4..edca1f5 100644 --- a/src/components/Steppers/ContextStepper.jsx +++ b/src/components/Steppers/ContextStepper.jsx @@ -291,6 +291,16 @@ export default function ContextStepper({ // grid: Object.keys(poisMap).length > 0 && , // context: context.area, // }, + { + label: "Get Distance Matrix", + description: `Use the Distance Matrix API to get travel distances and times between multiple locations. Choose origins, destinations and travel mode to find distance and durations.`, + additional: + "List of origin - destination pairs whose fastest route is added to the context", + icon: , + form: , + grid: Object.keys(distanceMatrix).length > 0 && , + context: context.distance, + }, { label: "Get Alternative Routes", description: `Utilize the Directions API to find routes between two points. Choose origin, destination and travel mode to find possible routes between them.`, @@ -303,16 +313,7 @@ export default function ContextStepper({ ), context: context.direction, }, - { - label: "Get Distance Matrix", - description: `Use the Distance Matrix API to get travel distances and times between multiple locations. Choose origins, destinations and travel mode to find distance and durations.`, - additional: - "List of origin - destination pairs whose fastest route is added to the context", - icon: , - form: , - grid: Object.keys(distanceMatrix).length > 0 && , - context: context.distance, - }, + // { // label: "Set Query Parameters", // description: `Set the time, day, and location that should be used as a basis for answering questions. This will help provide more accurate and context-specific responses.`, From 7dfb2e654dab52d8d38fcd1f47e7eded9879d4f7 Mon Sep 17 00:00:00 2001 From: Mahir Labib Dihan Date: Fri, 9 Aug 2024 04:14:21 +0600 Subject: [PATCH 002/253] context + question save done --- src/api/mapApi.js | 19 + src/api/queryApi.js | 3 + src/app/home/context/page.js | 2 +- src/app/page/ContextGenerator.jsx | 2 +- src/components/Box/RouteSummary.jsx | 63 +- src/components/Buttons/PlaceAddButton.jsx | 28 +- src/components/Cards/NearbyCard.jsx | 15 +- src/components/Cards/NewDirectionCard.jsx | 210 +++ src/components/Cards/PlaceCard.jsx | 5 +- src/components/Forms/DirectionForm.jsx | 308 +++- src/components/Forms/DistanceForm.jsx | 115 +- src/components/Forms/NearbyForm.jsx | 365 ++-- src/components/Forms/QuestionAnswerForm.jsx | 145 +- src/components/Grids/DirectionGrid.jsx | 12 +- .../InputFields/AutocompleteSearchBox.jsx | 17 +- .../InputFields/CategorySelectionField.jsx | 16 +- .../InputFields/CorrectAnswerEditor.jsx | 31 +- src/components/InputFields/OptionsEditor.jsx | 117 +- .../InputFields/PlaceSelectionField.jsx | 7 +- src/components/InputFields/QuestionEditor.jsx | 22 +- .../InputFields/TravelSelectionField..jsx | 8 +- .../InputFields/TypeSelectionField.jsx | 99 +- src/components/Lists/PoiList.jsx | 8 +- src/components/Lists/RoutesList.jsx | 58 +- src/components/LoginPrompts.jsx | 2 +- src/components/SaveQuery.jsx | 2 +- src/components/Steppers/ContextStepper.jsx | 8 +- src/contexts/AppContext.jsx | 3 +- src/contexts/GlobalContext.jsx | 8 +- src/database/newexample.json | 1571 +++++++++++++++++ src/database/newtypes.json | 215 +++ src/database/textualFields.json | 54 + src/services/contextGeneratorService.js | 123 +- src/services/utils.js | 34 +- 34 files changed, 3170 insertions(+), 525 deletions(-) create mode 100644 src/components/Cards/NewDirectionCard.jsx create mode 100644 src/database/newexample.json create mode 100644 src/database/newtypes.json create mode 100644 src/database/textualFields.json diff --git a/src/api/mapApi.js b/src/api/mapApi.js index 0cf847b..31fd9ce 100644 --- a/src/api/mapApi.js +++ b/src/api/mapApi.js @@ -4,6 +4,9 @@ class MapApi extends Api { getDetails = async (place_id) => { return await this.get("/map/details/" + place_id); }; + getDetailsNew = async (place_id) => { + return await this.get("/map/details/new/" + place_id); + }; getNearby = async (params) => { return await this.get( "/map/nearby?lat=" + @@ -21,6 +24,9 @@ class MapApi extends Api { params.location ); }; + getNearbyNew = async (params) => { + return await this.post("/map/nearby/new", params); + }; getInside = async (params) => { if (params.type === "") return; return await this.get( @@ -34,6 +40,9 @@ class MapApi extends Api { search = async (query) => { return await this.get("/map/search?query=" + query); }; + searchNew = async (query) => { + return await this.post("/map/search/new", { query }); + }; getDistance = async (origin, destination, mode) => { return await this.get( "/map/distance?origin=" + @@ -44,6 +53,10 @@ class MapApi extends Api { mode ); }; + getDistanceNew = async (params) => { + console.log(params); + return await this.post("/map/distance/new", params); + }; getDirections = async (origin, destination, mode) => { return await this.get( "/map/directions?origin=" + @@ -54,6 +67,12 @@ class MapApi extends Api { mode ); }; + getDirectionsNew = async (params) => { + console.log(">", params); + const response = await this.post("/map/directions/new", params); + console.log("<", response); + return response; + }; } const mapApi = new MapApi(); export default mapApi; diff --git a/src/api/queryApi.js b/src/api/queryApi.js index 18f1af0..deb3975 100644 --- a/src/api/queryApi.js +++ b/src/api/queryApi.js @@ -10,6 +10,9 @@ class QueryApi extends Api { createQuery = async (body) => { return await this.post("/queries", body); }; + createNewQuery = async (body) => { + return await this.post("/queries/new", body); + }; createQueryWithEvaluation = async (body) => { return await this.post("/queries/evaluate", body); }; diff --git a/src/app/home/context/page.js b/src/app/home/context/page.js index 37a4b03..5e60421 100644 --- a/src/app/home/context/page.js +++ b/src/app/home/context/page.js @@ -5,7 +5,7 @@ import { GlobalContext } from "@/contexts/GlobalContext"; import ContextGeneratorService from "@/services/contextGeneratorService"; import ContextStepper from "@/components/Steppers/ContextStepper"; import { AppContext } from "@/contexts/AppContext"; -import example from "@/database/example.json"; +import example from "@/database/newexample.json"; import mapApi from "@/api/mapApi"; import dayjs from "dayjs"; import KeyStoreButton from "@/components/Buttons/KeyStoreButton"; diff --git a/src/app/page/ContextGenerator.jsx b/src/app/page/ContextGenerator.jsx index ecc68e4..5321dc9 100644 --- a/src/app/page/ContextGenerator.jsx +++ b/src/app/page/ContextGenerator.jsx @@ -4,7 +4,7 @@ import { GlobalContext } from "@/contexts/GlobalContext"; import ContextGeneratorService from "@/services/contextGeneratorService"; import ContextStepper from "@/components/Steppers/ContextStepper"; import { AppContext } from "@/contexts/AppContext"; -import example from "./example.json"; +import example from "./newexample.json"; import mapApi from "@/api/mapApi"; import dayjs from "dayjs"; import KeyStoreButton from "@/components/Buttons/KeyStoreButton"; diff --git a/src/components/Box/RouteSummary.jsx b/src/components/Box/RouteSummary.jsx index d7c722e..70343d7 100644 --- a/src/components/Box/RouteSummary.jsx +++ b/src/components/Box/RouteSummary.jsx @@ -4,19 +4,62 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { Box } from "@mui/material"; import { useContext } from "react"; -export default function RouteSummary({ from_id, to_id }) { +export default function RouteSummary({ + from_id, + to_id, + intermediates, + optimized, +}) { const { savedPlacesMap } = useContext(AppContext); return ( - -
- {savedPlacesMap[from_id].name} -
+ + +
+ {savedPlacesMap[from_id].displayName.text} +
+ + {intermediates?.length > 0 && + intermediates.map((intermediate, index) => ( + <> +
+ {savedPlacesMap[intermediate].displayName.text} +
+ + + ))} +
+ {savedPlacesMap[to_id].displayName.text} +
+
- - -
- {savedPlacesMap[to_id].name} -
+ {optimized && ( + +
+ {savedPlacesMap[from_id].displayName.text} +
+ + {optimized?.length > 0 && + optimized.map((intermediate, index) => ( + <> +
+ { + savedPlacesMap[intermediate].displayName + .text + } +
+ + + ))} +
+ {savedPlacesMap[to_id].displayName.text} +
+
+ )}
); } diff --git a/src/components/Buttons/PlaceAddButton.jsx b/src/components/Buttons/PlaceAddButton.jsx index 73e8a81..f5b9841 100644 --- a/src/components/Buttons/PlaceAddButton.jsx +++ b/src/components/Buttons/PlaceAddButton.jsx @@ -6,7 +6,7 @@ import { Add } from "@mui/icons-material"; import { LoadingButton } from "@mui/lab"; import { Button } from "@mui/material"; import { useContext, useState } from "react"; - +import textualFields from "@/database/textualFields.json"; export default function PlaceAddButton({ place_id }) { const { selectedPlacesMap, setSelectedPlacesMap } = useContext(GlobalContext); @@ -17,10 +17,10 @@ export default function PlaceAddButton({ place_id }) { setLoading(true); let details = savedPlacesMap[place_id]; if (details === undefined) { - const res = await mapApi.getDetails(place_id); + const res = await mapApi.getDetailsNew(place_id); if (res.success) { - details = res.data.result; - console.log(res.data.result); + details = res.data; + console.log(details); setSavedPlacesMap((prev) => ({ ...prev, [place_id]: details, @@ -37,32 +37,18 @@ export default function PlaceAddButton({ place_id }) { }; const handleAdd = (details) => { - const place_id = details["place_id"]; + const place_id = details["id"]; if (place_id === "" || selectedPlacesMap[place_id]) return; setSelectedPlacesMap((prev) => ({ ...prev, [place_id]: { selectedAttributes: Object.keys(details).filter( (key) => - details[key] !== null && - key !== "place_id" && - key !== "name" && - key !== "last_updated" && - key !== "user_ratings_total" && - key !== "types" && - key !== "search_vector" && - key !== "vicinity" + details[key] !== null && textualFields.includes(key) ), attributes: Object.keys(details).filter( (key) => - details[key] !== null && - key !== "place_id" && - key !== "name" && - key !== "last_updated" && - key !== "user_ratings_total" && - key !== "types" && - key !== "search_vector" && - key !== "vicinity" + details[key] !== null && textualFields.includes(key) ), }, })); diff --git a/src/components/Cards/NearbyCard.jsx b/src/components/Cards/NearbyCard.jsx index 3144a2a..a2cf723 100644 --- a/src/components/Cards/NearbyCard.jsx +++ b/src/components/Cards/NearbyCard.jsx @@ -51,8 +51,7 @@ function NearbyCardSummary({ index, place_id, entry, expanded }) { className="gap-1" > - {savedPlacesMap[place_id].name || - selectedPlacesMap[place_id].alias} + {savedPlacesMap[place_id].displayName.text} @@ -64,11 +63,7 @@ function NearbyCardSummary({ index, place_id, entry, expanded }) { diff --git a/src/components/Cards/NewDirectionCard.jsx b/src/components/Cards/NewDirectionCard.jsx new file mode 100644 index 0000000..020dc6c --- /dev/null +++ b/src/components/Cards/NewDirectionCard.jsx @@ -0,0 +1,210 @@ +import React, { useContext, useState } from "react"; +import { + Box, + Card, + CardContent, + Chip, + Collapse, + Divider, + IconButton, + List, + ListItem, + ListItemIcon, + ListItemText, + Paper, + Switch, + Typography, +} from "@mui/material"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faArrowDown } from "@fortawesome/free-solid-svg-icons"; +import { Delete, ExpandMore } from "@mui/icons-material"; +import { GlobalContext } from "@/contexts/GlobalContext"; +import { AppContext } from "@/contexts/AppContext"; +import { + convertTravelModeToIcon, + convertTravelModeToLabel, +} from "@/services/utils"; +import RoutesList from "../Lists/RoutesList"; +import RouteSummary from "../Box/RouteSummary"; + +function DirectionCardDetails({ direction, index }) { + const { directionInformation, setDirectionInformation } = + useContext(GlobalContext); + + return ( + + +
Show Steps
+ { + const newDirectionMatrix = [...directionInformation]; + newDirectionMatrix[index].showSteps = + !newDirectionMatrix[index].showSteps; + + console.log(newDirectionMatrix); + setDirectionInformation(newDirectionMatrix); + }} + checked={direction.showSteps} + size="small" + /> +
+ 1 + ? direction.routes[0].optimizedIntermediateWaypointIndex.map( + (i) => direction.intermediates[i] + ) + : direction.intermediates.map( + (intermediate) => intermediate + )), + direction.destination, + ]} + routes={direction.routes} + showSteps={direction.showSteps} + /> +
+ ); +} + +const avoidMap = { + avoidTolls: "Tolls", + avoidHighways: "Highways", + avoidFerries: "Ferries", +}; +const transitModeMap = { + BUS: "Bus", + LIGHT_RAIL: "Tram and Light rail", + SUBWAY: "Subway", + TRAIN: "Train", + // RAIL: "Rail", // This is equivalent to a combination of SUBWAY, TRAIN, and LIGHT_RAIL. +}; +function DirectionCardSummary({ direction, expanded, index }) { + const { directionInformation, setDirectionInformation } = + useContext(GlobalContext); + const { savedPlacesMap } = useContext(AppContext); + + return ( + <> + + 1 + ? direction.routes[0].optimizedIntermediateWaypointIndex.map( + (i) => direction.intermediates[i] + ) + : undefined, + }} + /> + + { + const newDirectionMatrix = [ + ...directionInformation, + ]; + newDirectionMatrix.splice(index, 1); + setDirectionInformation(newDirectionMatrix); + }} + size="small" + > + + + + + + + + + {convertTravelModeToIcon(direction.travelMode)} + + {["DRIVE", "TWO_WHEELER"].includes(direction.travelMode) && + Object.entries(direction.routeModifiers).map( + ([key, value]) => + value && ( + + ) + )} + {["TRANSIT"].includes(direction.travelMode) && + direction.transitPreferences.allowedTravelModes.map( + (mode) => ( + + ) + )} + + + ); +} +export default function DirectionCard({ direction, index }) { + const [expanded, setExpanded] = useState(index === 0); + return ( + + setExpanded(!expanded)} + className="cursor-pointer" + > + + + + + + + + ); +} diff --git a/src/components/Cards/PlaceCard.jsx b/src/components/Cards/PlaceCard.jsx index a6fe8b9..e71ce61 100644 --- a/src/components/Cards/PlaceCard.jsx +++ b/src/components/Cards/PlaceCard.jsx @@ -21,16 +21,15 @@ import PlaceDeleteButton from "../Buttons/PlaceDeleteButton"; function PlaceCardSummary({ placeId, expanded }) { const { selectedPlacesMap } = useContext(GlobalContext); - const { savedPlacesMap } = useContext(AppContext); return ( <> - {savedPlacesMap[placeId].name} + {savedPlacesMap[placeId].displayName.text} - {savedPlacesMap[placeId].formatted_address} + {savedPlacesMap[placeId].shortFormattedAddress} { - setNewDirection({ - from: "", - to: "", - travelMode: "walking", - departureTime: { - type: "now", - time: new Date(), - date: new Date(), - departureTimestamp: new Date(), - }, - }); + setNewDirection(initialData); }, [selectedPlacesMap]); const handleDirectionAdd = async () => { - if (newDirection.from === "" || newDirection.to === "") return; + if (newDirection.origin === "" || newDirection.destination === "") + return; // Fetch the direction between the two places from google maps - setLoading(true); - const response = await mapApi.getDirections( - newDirection.from, - newDirection.to, - newDirection.travelMode - ); - if (response.success) { + const response = await mapApi.getDirectionsNew(newDirection); + if (response.success && response.data.routes) { // console.log(response.) const routes = response.data.routes; - const newDirectionInfo = { ...directionInformation }; + const newDirectionInfo = [...directionInformation]; const all_routes = []; routes.forEach((route) => { - const steps = []; - route.legs[0].steps.forEach((step) => { - steps.push(step.html_instructions); + const legs = []; + route.legs.forEach((leg) => { + const steps = []; + console.log("Steps: ", route.legs[0].steps); + leg.steps.forEach((step) => { + console.log(step); + if (step.navigationInstruction) + steps.push(step.navigationInstruction.instructions); + }); + legs.push({ + steps, + }); }); + console.log("Legs: ", legs); all_routes.push({ - label: route.summary, - duration: route.legs[0].duration.text, - distance: route.legs[0].distance.text, - steps: steps, + label: route.description, + duration: route.localizedValues.staticDuration.text, + distance: route.localizedValues.distance.text, + legs: legs, + optimizedIntermediateWaypointIndex: + route.optimizedIntermediateWaypointIndex, }); }); - if (newDirectionInfo[newDirection.from]) - newDirectionInfo[newDirection.from][newDirection.to] = { - ...newDirectionInfo[newDirection.from][newDirection.to], - [newDirection.travelMode]: { - routes: all_routes, - showSteps: true, - }, - }; - else { - newDirectionInfo[newDirection.from] = { - [newDirection.to]: { - [newDirection.travelMode]: { - routes: all_routes, - showSteps: true, - }, - }, - }; - } + const o = newDirection.origin; + const d = newDirection.destination; + + newDirectionInfo.push({ + origin: newDirection.origin, + destination: newDirection.destination, + intermediates: newDirection.intermediates, + travelMode: newDirection.travelMode, + optimizeWaypointOrder: newDirection.optimizeWaypointOrder, + transitPreferences: newDirection.transitPreferences, + routeModifiers: { + avoidTolls: ["DRIVE", "TWO_WHEELER"].includes( + newDirection.travelMode + ) + ? newDirection.routeModifiers.avoidTolls + : false, + avoidHighways: ["DRIVE", "TWO_WHEELER"].includes( + newDirection.travelMode + ) + ? newDirection.routeModifiers.avoidHighways + : false, + avoidFerries: ["DRIVE", "TWO_WHEELER"].includes( + newDirection.travelMode + ) + ? newDirection.routeModifiers.avoidFerries + : false, + }, + routes: all_routes, + showSteps: false, + }); + + // if (newDirectionInfo[o]) + // newDirectionInfo[o][d] = { + // ...newDirectionInfo[o][d], + // [newDirection.travelMode]: { + + // routes: all_routes, + // showSteps: true, + // }, + // }; + // else { + // newDirectionInfo[o] = { + // [d]: { + // [newDirection.travelMode]: { + // routes: all_routes, + // showSteps: true, + // }, + // }, + // }; + // } setDirectionInformation(newDirectionInfo); } else { showError("Couldn't find directions between the two places"); @@ -95,11 +159,11 @@ export default function DirectionForm({ handlePlaceAdd }) { { setNewDirection((prev) => ({ ...prev, - from: event.target.value, + origin: event.target.value, })); }} handlePlaceAdd={handlePlaceAdd} @@ -109,11 +173,11 @@ export default function DirectionForm({ handlePlaceAdd }) { { setNewDirection((prev) => ({ ...prev, - to: event.target.value, + destination: event.target.value, })); }} handlePlaceAdd={handlePlaceAdd} @@ -132,6 +196,41 @@ export default function DirectionForm({ handlePlaceAdd }) { /> + {newDirection.travelMode !== "TRANSIT" && ( + <> + + { + setNewDirection((prev) => ({ + ...prev, + intermediates: event.target.value, + })); + }} + handlePlaceAdd={handlePlaceAdd} + multiple={true} + /> + + + { + setNewDirection((prev) => ({ + ...prev, + optimizeWaypointOrder: + !prev.optimizeWaypointOrder, + })); + }} + checked={newDirection.optimizeWaypointOrder} + size="small" + /> +
+ Optimize intermediates order +
+
+ + )} + {/* {(newDirection.travelMode === "transit" || newDirection.travelMode === "driving") && ( @@ -146,7 +245,8 @@ export default function DirectionForm({ handlePlaceAdd }) { /> )} */} - {newDirection.from && newDirection.to && ( + + {/* {newDirection.from && newDirection.to && ( - - )} */} + /> + ) : ( + + setNewNearbyPlaces((prev) => ({ + ...prev, + keyword: e.target.value, + })) + } + /> + )} +
- {/* - {true && ( - <> - Mark a location - + + )} - */} - + setNewNearbyPlaces((prev) => ({ ...prev, - type: newValue, + minRating: e.target.value, })) } + inputProps={{ step: 0.5, min: 0, max: 5 }} + size="small" + fullWidth /> - - {newNearbyPlaces.location && newNearbyPlaces.type && ( - - - - )} - + > + {Object.keys(priceMap).map((key) => ( + + -1 + } + /> + + + ))} + + + + The default is to select all price levels. + + setNewNearbyPlaces((prev) => ({ ...prev, - rankBy: e.target.value, + rankPreference: e.target.value, })) } > } label="Rank by Distance" /> } - label="Rank by Prominence" + label="Rank by Relevance" /> - {newNearbyPlaces.rankBy === "prominence" && ( + {/* {newNearbyPlaces.rankPreference === "prominence" && ( - )} + )} */} { + const newQuery = { + ...query, + context: ContextGeneratorService.convertContextToText(context), + context_json: { + saved_places: savedPlacesMap, + distance_matrix: distanceMatrix, + places: selectedPlacesMap, + nearby_places: nearbyPlacesMap, + current_information: currentInformation, + pois: poisMap, + directions: directionInformation, + }, + }; + + if (query.id === undefined) { + const res = await queryApi.createNewQuery(newQuery); + if (res.success) { + // update the queries + showSuccess("Query saved successfully"); + const newQueries = [...queries]; + newQueries.unshift(res.data[0]); + setQueries(newQueries); + handleDiscard(); + // router.push("/home/my-dataset"); + } else { + showError("Can't save this query"); + // window.scrollTo(0, 0); + } + } else { + const res = await queryApi.updateQuery(query.id, newQuery); + if (res.success) { + setQueries((prev) => + prev.map((q) => (q.id === res.data[0].id ? res.data[0] : q)) + ); + // update the queries + showSuccess("Query edited successfully"); + handleDiscard(); + // router.push("/home/my-dataset"); + } else { + showError("Can't update this query"); + // window.scrollTo(0, 0); + } + } + }; + + const handleLogin = () => { + router.push(config.logoutRedirect); + }; + + const handleDiscard = () => { + setQuery({ + questions: [initQuery], + }); + }; return (
- - - - - + {query.questions.map((question, index) => ( + + + + + + + + setQuery((prev) => { + const newQuery = { ...prev }; + newQuery.questions.splice(index, 1); + return newQuery; + }) + } + > + + + + + ))} + + + + {/* Clear - + */} + + {isAuthenticated ? ( + + ) : ( + + )} ); } diff --git a/src/components/Grids/DirectionGrid.jsx b/src/components/Grids/DirectionGrid.jsx index d9073f4..0e2f4d4 100644 --- a/src/components/Grids/DirectionGrid.jsx +++ b/src/components/Grids/DirectionGrid.jsx @@ -1,14 +1,20 @@ import React, { useContext } from "react"; import { Grid } from "@mui/material"; -import DirectionCard from "@/components/Cards/DirectionCard"; +import DirectionCard from "@/components/Cards/NewDirectionCard"; import { GlobalContext } from "@/contexts/GlobalContext"; export default function DirectionGrid() { const { directionInformation } = useContext(GlobalContext); + console.log(directionInformation); return ( - {Object.keys(directionInformation).map((from_id, i) => + {directionInformation.map((direction, i) => ( + + + + ))} + {/* {Object.keys(directionInformation).map((from_id, i) => Object.keys(directionInformation[from_id]).map((to_id, j) => ( )) - )} + )} */} ); } diff --git a/src/components/InputFields/AutocompleteSearchBox.jsx b/src/components/InputFields/AutocompleteSearchBox.jsx index 4fb7697..32178df 100644 --- a/src/components/InputFields/AutocompleteSearchBox.jsx +++ b/src/components/InputFields/AutocompleteSearchBox.jsx @@ -26,11 +26,11 @@ function SearchPlaceCard({ place, index, length }) { - + @@ -64,14 +64,11 @@ export default function AutocompleteSearchBox() { const searchMap = async (query) => { setLoading(true); - const response = await mapApi.search(query); + const response = await mapApi.searchNew(query); if (response.success) { - setMapResults([...response.data.results]); - // setCache((prev) => ({ - // ...prev, - // [query]: response.data.results, - // })); - if (response.data.results.length === 0) { + setMapResults([...response.data.places]); + console.log("Map Results: ", response.data); + if (response.data.places.length === 0) { setNotFound(true); } } else { diff --git a/src/components/InputFields/CategorySelectionField.jsx b/src/components/InputFields/CategorySelectionField.jsx index b56b6f5..b328a11 100644 --- a/src/components/InputFields/CategorySelectionField.jsx +++ b/src/components/InputFields/CategorySelectionField.jsx @@ -74,7 +74,7 @@ import IconButton from "@mui/material/IconButton"; import Tooltip from "@mui/material/Tooltip"; import { useRouter } from "next/navigation"; -export default function CategorySelectionField() { +export default function CategorySelectionField({ index }) { const { query, setQuery } = useContext(GlobalContext); const router = useRouter(); const navigateToHelpPage = () => { @@ -95,12 +95,14 @@ export default function CategorySelectionField() { placeholder="Choose a category of the question" id="outlined-adornment" className="outlined-input" - value={query.classification} + value={query.questions[index].classification} onChange={(e) => { - setQuery((prev) => ({ - ...prev, - classification: e.target.value, - })); + setQuery((prev) => { + const newQuery = { ...prev }; + newQuery.questions[index].classification = + e.target.value; + return newQuery; + }); }} > {categories.map((value, index) => ( @@ -125,4 +127,4 @@ export default function CategorySelectionField() { ); -} \ No newline at end of file +} diff --git a/src/components/InputFields/CorrectAnswerEditor.jsx b/src/components/InputFields/CorrectAnswerEditor.jsx index 375a89f..82aa231 100644 --- a/src/components/InputFields/CorrectAnswerEditor.jsx +++ b/src/components/InputFields/CorrectAnswerEditor.jsx @@ -8,7 +8,7 @@ import { } from "@mui/material"; import { GlobalContext } from "@/contexts/GlobalContext"; -export default function CorrectAnswerEditor() { +export default function CorrectAnswerEditor({ index }) { const { query, setQuery } = useContext(GlobalContext); return ( @@ -16,16 +16,25 @@ export default function CorrectAnswerEditor() { Correct Answer: { - console.log(e.target.value, typeof e.target.value); - setQuery((prev) => ({ - ...prev, - answer: { - ...prev.answer, - correct: parseInt(e.target.value), - }, - })); + // console.log(e.target.value, typeof e.target.value); + + setQuery((prev) => { + const newQuery = { ...prev }; + newQuery.questions[index].answer.correct = parseInt( + e.target.value + ); + return newQuery; + }); + + // setQuery((prev) => ({ + // ...prev, + // answer: { + // ...prev.answer, + // correct: parseInt(e.target.value), + // }, + // })); }} > } label={`None`} /> - {query.answer.options.map((option, index) => ( + {query.questions[index].answer.options.map((option, index) => ( { + const handleOptionChange = (i, value) => { setQuery((prev) => { - const options = [...prev.answer.options]; - options[index] = value; + const options = [...prev.questions[index].answer.options]; + options[i] = value; return { ...prev, - answer: { - ...prev.answer, - options, - }, + questions: [ + ...prev.questions.slice(0, index), + { + ...prev.questions[index], + answer: { + ...prev.questions[index].answer, + options, + }, + }, + ...prev.questions.slice(index + 1), + ], }; }); }; const addOption = () => { - setQuery((prev) => ({ - ...prev, - answer: { - ...prev.answer, - options: [...prev.answer.options, ""], - }, - })); + setQuery((prev) => { + const options = [...prev.questions[index].answer.options, ""]; + return { + ...prev, + questions: [ + ...prev.questions.slice(0, index), + { + ...prev.questions[index], + answer: { + ...prev.questions[index].answer, + options, + }, + }, + ...prev.questions.slice(index + 1), + ], + }; + }); }; - const removeOption = (index) => { - if (query.answer.correct === index) { - setQuery((prev) => ({ - ...prev, - answer: { - ...prev.answer, - correct: -1, - }, - })); + const removeOption = (i) => { + if (query.questions[index].answer.correct === i) { + setQuery((prev) => { + const answer = [...prev.questions[index].answer]; + answer.correct = -1; + return { + ...prev, + questions: [ + ...prev.questions.slice(0, index), + { + ...prev.questions[index], + answer, + }, + ...prev.questions.slice(index + 1), + ], + }; + }); } + setQuery((prev) => { - const options = [...prev.answer.options]; - options.splice(index, 1); + const options = [...prev.questions[index].answer.options]; + options.splice(i, 1); return { ...prev, - answer: { - ...prev.answer, - options, - }, + questions: [ + ...prev.questions.slice(0, index), + { + ...prev.questions[index], + answer: { + ...prev.questions[index].answer, + options, + }, + }, + ...prev.questions.slice(index + 1), + ], }; }); + // setQuery((prev) => { + // const options = [...prev.answer.options]; + // options.splice(i, 1); + // return { + // ...prev, + // answer: { + // ...prev.answer, + // options, + // }, + // }; + // }); }; return ( Options: - {query.answer.options.map((option, index) => ( + {query.questions[index].answer.options.map((option, i) => ( - handleOptionChange(index, e.target.value) - } + onChange={(e) => handleOptionChange(i, e.target.value)} required InputProps={{ endAdornment: ( removeOption(index)} + onClick={() => removeOption(i)} sx={{ ml: 1 }} > diff --git a/src/components/InputFields/PlaceSelectionField.jsx b/src/components/InputFields/PlaceSelectionField.jsx index 8a76c34..08d7589 100644 --- a/src/components/InputFields/PlaceSelectionField.jsx +++ b/src/components/InputFields/PlaceSelectionField.jsx @@ -17,7 +17,7 @@ export default function PlaceSelectionField({ return ( <> - + {label} diff --git a/src/components/InputFields/QuestionEditor.jsx b/src/components/InputFields/QuestionEditor.jsx index 06fe62b..84347ec 100644 --- a/src/components/InputFields/QuestionEditor.jsx +++ b/src/components/InputFields/QuestionEditor.jsx @@ -12,7 +12,7 @@ import ContextGeneratorService from "@/services/contextGeneratorService"; import { showError } from "@/contexts/ToastProvider"; import gptApi from "@/api/gptApi"; -export default function QuestionEditor() { +export default function QuestionEditor({ index }) { const { query, setQuery } = useContext(GlobalContext); const [isGenerating, setIsGenerating] = useState(false); const { context } = useContext(GlobalContext); @@ -24,10 +24,11 @@ export default function QuestionEditor() { } else { const res = await gptApi.generateQuestion(text); if (res.success) { - setQuery((prev) => ({ - ...prev, - question: res.data, - })); + setQuery((prev) => { + const newQuery = { ...prev }; + newQuery.questions[index].title = res.data; + return newQuery; + }); } else { showError(res.message); } @@ -44,12 +45,13 @@ export default function QuestionEditor() { - setQuery((prev) => ({ - ...prev, - question: e.target.value, - })) + setQuery((prev) => { + const newQuery = { ...prev }; + newQuery.questions[index].title = e.target.value; + return newQuery; + }) } multiline required diff --git a/src/components/InputFields/TravelSelectionField..jsx b/src/components/InputFields/TravelSelectionField..jsx index a7b3636..3f5e57d 100644 --- a/src/components/InputFields/TravelSelectionField..jsx +++ b/src/components/InputFields/TravelSelectionField..jsx @@ -25,10 +25,10 @@ export default function TravelSelectionField({ mode, setMode }) { input={} > {[ - { value: "walking", label: "Walking" }, - { value: "driving", label: "Driving" }, - { value: "bicycling", label: "Bicycling" }, - { value: "transit", label: "Public Transport" }, + { value: "WALK", label: "Walking" }, + { value: "DRIVE", label: "Driving" }, + { value: "BICYCLE", label: "Bicycling" }, + { value: "TWO_WHEELER", label: "Two Wheeler" }, ].map((mode, index) => ( {mode.label} diff --git a/src/components/InputFields/TypeSelectionField.jsx b/src/components/InputFields/TypeSelectionField.jsx index 07e3587..b93f61b 100644 --- a/src/components/InputFields/TypeSelectionField.jsx +++ b/src/components/InputFields/TypeSelectionField.jsx @@ -1,30 +1,79 @@ -import placeTypes from "@/database/types.json"; -import { Autocomplete, TextField } from "@mui/material"; +import types from "@/database/newtypes.json"; +import { + Autocomplete, + FormControl, + InputLabel, + ListSubheader, + MenuItem, + Select, + TextField, +} from "@mui/material"; import { convertFromSnake } from "@/services/utils"; +import React from "react"; + +const placeTypes = []; +for (const category in types) { + placeTypes.push(...types[category]); +} +// export default function TypeSelectionField({ type, setType }) { +// return ( +// +// a.localeCompare(b, "en", { sensitivity: "base" }) +// )} +// fullWidth +// freeSolo +// value={type} +// getOptionLabel={(option) => `${convertFromSnake(option)}`} +// onChange={(e, newValue) => { +// setType(newValue); +// }} +// renderInput={(params) => ( +// { +// setType(e.target.value); +// }} +// /> +// )} +// /> +// ); +// } + export default function TypeSelectionField({ type, setType }) { return ( - `${convertFromSnake(option)}`} - onChange={(e, newValue) => { - setType(newValue); - }} - renderInput={(params) => ( - { - setType(e.target.value); - }} - /> - )} - /> + + Type + + ); } diff --git a/src/components/Lists/PoiList.jsx b/src/components/Lists/PoiList.jsx index 2b9fc78..31f36a7 100644 --- a/src/components/Lists/PoiList.jsx +++ b/src/components/Lists/PoiList.jsx @@ -26,7 +26,13 @@ export default function PoiList({ places, handleTogglePlace }) { /> diff --git a/src/components/Lists/RoutesList.jsx b/src/components/Lists/RoutesList.jsx index 26b3561..889eb69 100644 --- a/src/components/Lists/RoutesList.jsx +++ b/src/components/Lists/RoutesList.jsx @@ -1,7 +1,17 @@ import React, { useContext, useState } from "react"; -import { Divider, List, ListItem, ListItemText } from "@mui/material"; +import { + Divider, + List, + ListItem, + ListItemText, + Typography, +} from "@mui/material"; +import { GlobalContext } from "@/contexts/GlobalContext"; +import { ArrowForward, ArrowRight, ArrowRightAlt, DoubleArrow, KeyboardDoubleArrowRight } from "@mui/icons-material"; +import { AppContext } from "@/contexts/AppContext"; -export default function RoutesList({ routes, showSteps }) { +export default function RoutesList({ routes, showSteps, waypoints }) { + const { savedPlacesMap } = useContext(AppContext); return ( {routes.map((route, index) => ( @@ -26,14 +36,42 @@ export default function RoutesList({ routes, showSteps }) { secondary={ showSteps && (
- {route.steps.map((step, index1) => ( -

+ {route.legs.map((leg, i) => ( + <> + {route.legs.length > 1 && ( + + { + savedPlacesMap[ + waypoints[i] + ].displayName + .text + } + + { + savedPlacesMap[ + waypoints[ + i + 1 + ] + ].displayName + .text + } + + )} + + {leg.steps.map( + (step, j) => ( +

+ ) + )} + ))}

) diff --git a/src/components/LoginPrompts.jsx b/src/components/LoginPrompts.jsx index 9210043..adbe5ff 100644 --- a/src/components/LoginPrompts.jsx +++ b/src/components/LoginPrompts.jsx @@ -5,7 +5,7 @@ import LockIcon from "@mui/icons-material/Lock"; const LoginPrompt = ({ onLogin }) => { return ( - + Want to save this evaluation? diff --git a/src/components/SaveQuery.jsx b/src/components/SaveQuery.jsx index 2aec163..9441f1a 100644 --- a/src/components/SaveQuery.jsx +++ b/src/components/SaveQuery.jsx @@ -13,7 +13,7 @@ const SaveQuery = ({ onSave, onDiscard }) => { }; return ( - + Save this evaluation? diff --git a/src/components/Steppers/ContextStepper.jsx b/src/components/Steppers/ContextStepper.jsx index edca1f5..0f618a1 100644 --- a/src/components/Steppers/ContextStepper.jsx +++ b/src/components/Steppers/ContextStepper.jsx @@ -160,7 +160,7 @@ function ContextStep({ > {index === 0 ? "Start" : "Continue"} - {/* {index > 0 ? ( + {index > 0 ? ( - )} + )} */} diff --git a/src/contexts/AppContext.jsx b/src/contexts/AppContext.jsx index 71c65b3..cb05ba1 100644 --- a/src/contexts/AppContext.jsx +++ b/src/contexts/AppContext.jsx @@ -548,8 +548,7 @@ export default function AppContextProvider({ children }) { }; useEffect(() => { - // if (process.env.NODE_ENV === "production") - { + if (process.env.NODE_ENV === "production") { fetchQueries(); // fetchPlaces(); } diff --git a/src/contexts/GlobalContext.jsx b/src/contexts/GlobalContext.jsx index 58c0c93..6afeb84 100644 --- a/src/contexts/GlobalContext.jsx +++ b/src/contexts/GlobalContext.jsx @@ -177,7 +177,7 @@ export default function GlobalContextProvider({ children }) { const initNearbyPlacesMap = {}; const initPoisMap = {}; const initDistanceMatrix = {}; - const initDirectionInformation = {}; + const initDirectionInformation = []; const initCurrentInformation = { time: null, day: "", @@ -224,7 +224,7 @@ export default function GlobalContextProvider({ children }) { // }; const initQuery = { - question: "", + title: "", answer: { type: "mcq", options: ["", "", "", ""], @@ -233,7 +233,9 @@ export default function GlobalContextProvider({ children }) { classification: "", }; - const [query, setQuery] = useState(initQuery); + const [query, setQuery] = useState({ + questions: [initQuery], + }); const { savedPlacesMap, setSavedPlacesMap } = useContext(AppContext); diff --git a/src/database/newexample.json b/src/database/newexample.json new file mode 100644 index 0000000..da669e0 --- /dev/null +++ b/src/database/newexample.json @@ -0,0 +1,1571 @@ +{ + "saved_places": { + "ChIJD3uTd9hx5kcR1IQvGfr8dbk": { + "id": "ChIJD3uTd9hx5kcR1IQvGfr8dbk", + "displayName": { + "text": "Louvre Museum", + "languageCode": "en" + }, + "name": "places/ChIJD3uTd9hx5kcR1IQvGfr8dbk", + "addressComponents": [ + { + "longText": "Paris", + "shortText": "Paris", + "types": ["locality", "political"], + "languageCode": "en" + }, + { + "longText": "Paris", + "shortText": "Paris", + "types": ["administrative_area_level_2", "political"], + "languageCode": "en" + }, + { + "longText": "Île-de-France", + "shortText": "IDF", + "types": ["administrative_area_level_1", "political"], + "languageCode": "en" + }, + { + "longText": "France", + "shortText": "FR", + "types": ["country", "political"], + "languageCode": "en" + }, + { + "longText": "75001", + "shortText": "75001", + "types": ["postal_code"], + "languageCode": "en-US" + } + ], + "adrFormatAddress": "75001 Paris, France", + "shortFormattedAddress": "Cour Carrée, Paris", + "formattedAddress": "75001 Paris, France", + "location": { + "latitude": 48.8606111, + "longitude": 2.337644 + }, + "plusCode": { + "globalCode": "8FW4V86Q+63", + "compoundCode": "V86Q+63 Paris, France" + }, + "types": [ + "museum", + "tourist_attraction", + "point_of_interest", + "establishment" + ], + "viewport": { + "low": { + "latitude": 48.8595509697085, + "longitude": 2.3337294999999982 + }, + "high": { + "latitude": 48.8622489302915, + "longitude": 2.3398489000000016 + } + }, + "accessibilityOptions": { + "wheelchairAccessibleParking": true, + "wheelchairAccessibleEntrance": true, + "wheelchairAccessibleRestroom": true + }, + "businessStatus": "OPERATIONAL", + "googleMapsUri": "https://maps.google.com/?cid=13363865620386383060", + "iconBackgroundColor": "#13B5C7", + "iconMaskBaseUri": "https://maps.gstatic.com/mapfiles/place_api/icons/v2/museum_pinlet", + "primaryType": "museum", + "primaryTypeDisplayName": { + "text": "Museum", + "languageCode": "en" + }, + "subDestinations": null, + "utcOffsetMinutes": 120, + "currentOpeningHours": { + "openNow": true, + "periods": [ + { + "open": { + "day": 0, + "hour": 9, + "minute": 0, + "date": { + "year": 2024, + "month": 8, + "day": 11 + } + }, + "close": { + "day": 0, + "hour": 18, + "minute": 0, + "date": { + "year": 2024, + "month": 8, + "day": 11 + } + } + }, + { + "open": { + "day": 1, + "hour": 9, + "minute": 0, + "date": { + "year": 2024, + "month": 8, + "day": 12 + } + }, + "close": { + "day": 1, + "hour": 18, + "minute": 0, + "date": { + "year": 2024, + "month": 8, + "day": 12 + } + } + }, + { + "open": { + "day": 3, + "hour": 9, + "minute": 0, + "date": { + "year": 2024, + "month": 8, + "day": 14 + } + }, + "close": { + "day": 3, + "hour": 21, + "minute": 0, + "date": { + "year": 2024, + "month": 8, + "day": 14 + } + } + }, + { + "open": { + "day": 4, + "hour": 9, + "minute": 0, + "date": { + "year": 2024, + "month": 8, + "day": 8 + } + }, + "close": { + "day": 4, + "hour": 18, + "minute": 0, + "date": { + "year": 2024, + "month": 8, + "day": 8 + } + } + }, + { + "open": { + "day": 5, + "hour": 9, + "minute": 0, + "date": { + "year": 2024, + "month": 8, + "day": 9 + } + }, + "close": { + "day": 5, + "hour": 21, + "minute": 0, + "date": { + "year": 2024, + "month": 8, + "day": 9 + } + } + }, + { + "open": { + "day": 6, + "hour": 9, + "minute": 0, + "date": { + "year": 2024, + "month": 8, + "day": 10 + } + }, + "close": { + "day": 6, + "hour": 18, + "minute": 0, + "date": { + "year": 2024, + "month": 8, + "day": 10 + } + } + } + ], + "weekdayDescriptions": [ + "Monday: 9:00 AM – 6:00 PM", + "Tuesday: Closed", + "Wednesday: 9:00 AM – 9:00 PM", + "Thursday: 9:00 AM – 6:00 PM", + "Friday: 9:00 AM – 9:00 PM", + "Saturday: 9:00 AM – 6:00 PM", + "Sunday: 9:00 AM – 6:00 PM" + ] + }, + "currentSecondaryOpeningHours": null, + "internationalPhoneNumber": "+33 1 40 20 53 17", + "nationalPhoneNumber": "01 40 20 53 17", + "priceLevel": null, + "rating": 4.7, + "regularOpeningHours": { + "openNow": true, + "periods": [ + { + "open": { + "day": 0, + "hour": 9, + "minute": 0 + }, + "close": { + "day": 0, + "hour": 18, + "minute": 0 + } + }, + { + "open": { + "day": 1, + "hour": 9, + "minute": 0 + }, + "close": { + "day": 1, + "hour": 18, + "minute": 0 + } + }, + { + "open": { + "day": 3, + "hour": 9, + "minute": 0 + }, + "close": { + "day": 3, + "hour": 21, + "minute": 0 + } + }, + { + "open": { + "day": 4, + "hour": 9, + "minute": 0 + }, + "close": { + "day": 4, + "hour": 18, + "minute": 0 + } + }, + { + "open": { + "day": 5, + "hour": 9, + "minute": 0 + }, + "close": { + "day": 5, + "hour": 21, + "minute": 0 + } + }, + { + "open": { + "day": 6, + "hour": 9, + "minute": 0 + }, + "close": { + "day": 6, + "hour": 18, + "minute": 0 + } + } + ], + "weekdayDescriptions": [ + "Monday: 9:00 AM – 6:00 PM", + "Tuesday: Closed", + "Wednesday: 9:00 AM – 9:00 PM", + "Thursday: 9:00 AM – 6:00 PM", + "Friday: 9:00 AM – 9:00 PM", + "Saturday: 9:00 AM – 6:00 PM", + "Sunday: 9:00 AM – 6:00 PM" + ] + }, + "regularSecondaryOpeningHours": null, + "userRatingCount": 314993, + "websiteUri": "https://www.louvre.fr/", + "allowsDogs": null, + "curbsidePickup": null, + "delivery": null, + "dineIn": null, + "editorialSummary": { + "text": "Former historic palace housing huge art collection, from Roman sculptures to da Vinci's \"Mona Lisa.\"", + "languageCode": "en" + }, + "evChargeOptions": null, + "fuelOptions": null, + "goodForChildren": true, + "goodForGroups": null, + "goodForWatchingSports": null, + "liveMusic": null, + "menuForChildren": null, + "parkingOptions": null, + "paymentOptions": null, + "outdoorSeating": null, + "reservable": null, + "restroom": true, + "reviews": [ + { + "name": "places/ChIJD3uTd9hx5kcR1IQvGfr8dbk/reviews/ChZDSUhNMG9nS0VJQ0FnSUNiN05YQ0hBEAE", + "relativePublishTimeDescription": "2 weeks ago", + "rating": 5, + "text": { + "text": "Visiting the louvre and seeing the Mona Lisa has always been one thing on my bucket list. Tickets were reasonable and we paid 22 Euros per person including audio. If you are in Paris, this is a must-do activity! Highly recommended!", + "languageCode": "en" + }, + "originalText": { + "text": "Visiting the louvre and seeing the Mona Lisa has always been one thing on my bucket list. Tickets were reasonable and we paid 22 Euros per person including audio. If you are in Paris, this is a must-do activity! Highly recommended!", + "languageCode": "en" + }, + "authorAttribution": { + "displayName": "Christian Baliko", + "uri": "https://www.google.com/maps/contrib/112080517843412335159/reviews", + "photoUri": "https://lh3.googleusercontent.com/a-/ALV-UjUYMcqPideRZ_YXRo65Eh2OqjsTACSASeibc-ulEzI8RW_0_Wm8=s128-c0x00000000-cc-rp-mo-ba5" + }, + "publishTime": "2024-07-24T10:36:18Z" + }, + { + "name": "places/ChIJD3uTd9hx5kcR1IQvGfr8dbk/reviews/ChZDSUhNMG9nS0VJQ0FnSUN6N01DQ09nEAE", + "relativePublishTimeDescription": "a week ago", + "rating": 5, + "text": { + "text": "Oh wow. Family of 4 with our son having a disability. We got to skip the queue.\nWe got about two hours to look around before he became bored.\nIt is amazing. Staff were kind to us.\nIt is worth going to see. I always thought the Mona Lisa was bigger.\nLoads of rooms and yes you can get lost but it is a joy to behold.\nWe got to stand close to the Mona. My daughter loved it as did we.\nWell worth the visit.\nIf coming as a family, there are alot of steps.\nIt is a long queue to get in but worth it. There are 3 different areas.\nIt was a great few hours but i would love to visit it and spend a good day walking around the different areas", + "languageCode": "en" + }, + "originalText": { + "text": "Oh wow. Family of 4 with our son having a disability. We got to skip the queue.\nWe got about two hours to look around before he became bored.\nIt is amazing. Staff were kind to us.\nIt is worth going to see. I always thought the Mona Lisa was bigger.\nLoads of rooms and yes you can get lost but it is a joy to behold.\nWe got to stand close to the Mona. My daughter loved it as did we.\nWell worth the visit.\nIf coming as a family, there are alot of steps.\nIt is a long queue to get in but worth it. There are 3 different areas.\nIt was a great few hours but i would love to visit it and spend a good day walking around the different areas", + "languageCode": "en" + }, + "authorAttribution": { + "displayName": "david cleary", + "uri": "https://www.google.com/maps/contrib/103995958631172260664/reviews", + "photoUri": "https://lh3.googleusercontent.com/a-/ALV-UjVNgx5t-aKcwH4CvsAkGR35P_knzXjBBXNNH4gry8cef78Sna-o=s128-c0x00000000-cc-rp-mo-ba5" + }, + "publishTime": "2024-07-30T13:30:40Z" + }, + { + "name": "places/ChIJD3uTd9hx5kcR1IQvGfr8dbk/reviews/ChdDSUhNMG9nS0VJQ0FnSUNicXUycjJRRRAB", + "relativePublishTimeDescription": "a week ago", + "rating": 5, + "text": { + "text": "A exquisite museum like no other. Louvre is an extremely beautiful museum, I would say that the museum is art in itself in the way that Louvre alone is so beautiful, along with the interior of the museum. Louvre is huge, like you could get lost in there, and just would therefore highly recommend buying tickets early and securing a ticket for when the museum opens because you could literally spend a whole day in the Louvre. Louvre has amazing art from all over the world, and from all time periods. Louvre is a must visit when in Paris. Tickets are also affordable.\nPS: If you are going to the Louvre for Mona Lisa I recommend getting there early and heading straight to her, or else there will be a long line to get a picture of her.", + "languageCode": "en" + }, + "originalText": { + "text": "A exquisite museum like no other. Louvre is an extremely beautiful museum, I would say that the museum is art in itself in the way that Louvre alone is so beautiful, along with the interior of the museum. Louvre is huge, like you could get lost in there, and just would therefore highly recommend buying tickets early and securing a ticket for when the museum opens because you could literally spend a whole day in the Louvre. Louvre has amazing art from all over the world, and from all time periods. Louvre is a must visit when in Paris. Tickets are also affordable.\nPS: If you are going to the Louvre for Mona Lisa I recommend getting there early and heading straight to her, or else there will be a long line to get a picture of her.", + "languageCode": "en" + }, + "authorAttribution": { + "displayName": "Alise Hunskaar", + "uri": "https://www.google.com/maps/contrib/104896993780901482006/reviews", + "photoUri": "https://lh3.googleusercontent.com/a-/ALV-UjV3ghnesxiSND8CdO9KunqDf6oCrxQ5eHPkbbDQ9pF0P3cIQgWg=s128-c0x00000000-cc-rp-mo-ba4" + }, + "publishTime": "2024-07-25T20:17:25Z" + }, + { + "name": "places/ChIJD3uTd9hx5kcR1IQvGfr8dbk/reviews/ChZDSUhNMG9nS0VJQ0FnSUNiMHJUX2F3EAE", + "relativePublishTimeDescription": "2 weeks ago", + "rating": 5, + "text": { + "text": "Louvre is my favorite museum in Paris. It is very interesting and a lot to see. This time, I have rested in the Studio and watched kids to play while charging my phone. It is on the Richelieu wing if you don’t know it.", + "languageCode": "en" + }, + "originalText": { + "text": "Louvre is my favorite museum in Paris. It is very interesting and a lot to see. This time, I have rested in the Studio and watched kids to play while charging my phone. It is on the Richelieu wing if you don’t know it.", + "languageCode": "en" + }, + "authorAttribution": { + "displayName": "Celia SO Paris private tours guide", + "uri": "https://www.google.com/maps/contrib/116876383728417378433/reviews", + "photoUri": "https://lh3.googleusercontent.com/a-/ALV-UjW0PlModeuVcoK7TpEcSKRJtIY_CawCOZQhwVUOBB4INRdyvkFw=s128-c0x00000000-cc-rp-mo-ba4" + }, + "publishTime": "2024-07-25T09:27:20Z" + }, + { + "name": "places/ChIJD3uTd9hx5kcR1IQvGfr8dbk/reviews/ChdDSUhNMG9nS0VJQ0FnSURiNHBxRW9BRRAB", + "relativePublishTimeDescription": "in the last week", + "rating": 3, + "text": { + "text": "The museum was quite large, and I especially enjoyed seeing the famous Monalisa photo. However, I was disappointed with the outdoor customer service. The staff seemed to be inattentive and didn't greet visitors who had purchased tickets with courtesy. Their behavior made me feel unwelcome, and it detracted from the overall experience.", + "languageCode": "en" + }, + "originalText": { + "text": "The museum was quite large, and I especially enjoyed seeing the famous Monalisa photo. However, I was disappointed with the outdoor customer service. The staff seemed to be inattentive and didn't greet visitors who had purchased tickets with courtesy. Their behavior made me feel unwelcome, and it detracted from the overall experience.", + "languageCode": "en" + }, + "authorAttribution": { + "displayName": "SOHEL", + "uri": "https://www.google.com/maps/contrib/110535307064609603356/reviews", + "photoUri": "https://lh3.googleusercontent.com/a-/ALV-UjWZzsH4VGE9OJHCTp_7bOb9e_EP2l2bX-bjP6iyLym_xo9-maTVgw=s128-c0x00000000-cc-rp-mo-ba5" + }, + "publishTime": "2024-08-03T15:53:47Z" + } + ], + "servesBeer": null, + "servesBreakfast": null, + "servesBrunch": null, + "servesCocktails": null, + "servesCoffee": null, + "servesDessert": null, + "servesDinner": null, + "servesLunch": null, + "servesVegetarianFood": null, + "servesWine": null, + "takeout": null, + "generativeSummary": null, + "areaSummary": null, + "updatedAt": "2024-08-08T15:57:17.264Z", + "photos": [ + { + "name": "places/ChIJD3uTd9hx5kcR1IQvGfr8dbk/photos/AelY_CtKLbe0Vk_6qX-7ykqfnG6bwuzSRFxPmZ0gwEO28fEco5x4KxghwWmjCQSp1gedKd4Ji4UPs9LJjooYazsSWDmehxP-_p6Ci-y8gIHKYvPeB9ZnT6ixdhNHAowbdp6TCHUZCg3RNSdTZN9WCgBpiMFLo01yS23Cuc60", + "widthPx": 4032, + "heightPx": 3024, + "authorAttributions": [ + { + "displayName": "Mirela Vieru", + "uri": "//maps.google.com/maps/contrib/104553014193855628750", + "photoUri": "//lh3.googleusercontent.com/a-/ALV-UjUn1M5lqvqKGkmVKJkgujb7XZ5mltSGV6VybyAAK2X001F52oFUqQ=s100-p-k-no-mo" + } + ] + }, + { + "name": "places/ChIJD3uTd9hx5kcR1IQvGfr8dbk/photos/AelY_CvvDVJddiiDCslxLRegsqZRHhLC-tDqeb55nw4pkNHN3TJuuAZMpAcd4Qe7oiPjjsbg4XjEbtAsyLmeTzU_L9KwuyCTJzshXqS9cr7WbzX8rIctooOE4H1ShY-s7h4eIg1fMROzEEp_nCVJjfSSH6twWQppzvwtBV8E", + "widthPx": 3264, + "heightPx": 2448, + "authorAttributions": [ + { + "displayName": "stefania uwechie", + "uri": "//maps.google.com/maps/contrib/112608409098339129907", + "photoUri": "//lh3.googleusercontent.com/a-/ALV-UjVDZyEyVt8IIVOD0oqOQhgZk7FmVRlPvF7Mz7pCWTUNEUjpd0S6cA=s100-p-k-no-mo" + } + ] + }, + { + "name": "places/ChIJD3uTd9hx5kcR1IQvGfr8dbk/photos/AelY_CtVHmNVhF2tCI84eCUtVinhef2U5rXHJ_jx06T7EUf_kU2tZXQQav1GI48iuiUNNxel4W_RwOK26AP0ILvAzL_jjd4oVo5PFzM_zr4NSCApLF0ufTHom4NuYsUyUSL-PjUIB8QcYOU2AckjEvLWyYk291HXN1AbV5Qw", + "widthPx": 3456, + "heightPx": 4608, + "authorAttributions": [ + { + "displayName": "Dicky Heriyana S", + "uri": "//maps.google.com/maps/contrib/110088307714047974679", + "photoUri": "//lh3.googleusercontent.com/a-/ALV-UjXaIfiw5EfVpCfMJPlSHWctpZhWWG6cq-QWu3hbJ0sME_3M1USI=s100-p-k-no-mo" + } + ] + }, + { + "name": "places/ChIJD3uTd9hx5kcR1IQvGfr8dbk/photos/AelY_Ctz1a3zuecpUGl3zEK6FZGT1SKmiKwj2gI3SA5HSuCkl64_6I04pjM9VXBVaEgjNkRvKFBkjTNy6NIAciKysSWbb3TR_uEmyxWwhR1WHjZLEHCiFEtEzZqPRtc8EywSiItAhotsF9VzuL91cxao9N7aKocaVXpXc7bl", + "widthPx": 2304, + "heightPx": 1536, + "authorAttributions": [ + { + "displayName": "Vincenzo Sassi", + "uri": "//maps.google.com/maps/contrib/116157449160302525695", + "photoUri": "//lh3.googleusercontent.com/a-/ALV-UjWO3bszWN9nfW0JL-H_Y8LW99d0S79q5FquLnGW3Lt1Z8RxhUYh=s100-p-k-no-mo" + } + ] + }, + { + "name": "places/ChIJD3uTd9hx5kcR1IQvGfr8dbk/photos/AelY_CvmlEzXl2t07PlQGiWtruq7LxAD7-lU9fGNiqkvJl8Aflo33Q-HWErOirLijzOF80k3rwN1O_R2XdmT8Ir3ijrEpetjWx0sqQhe6Adwxv-RDzmwQgyCipbeBYVZ8rDe2zj-i2md_Q73WNuDFIX4PXhJW1791DKL88u7", + "widthPx": 4032, + "heightPx": 3024, + "authorAttributions": [ + { + "displayName": "Ali TURK", + "uri": "//maps.google.com/maps/contrib/118370737205283084975", + "photoUri": "//lh3.googleusercontent.com/a-/ALV-UjVGxikc5zVSRJS76KbPweQYYR8Q6eYKLux66CbGNPTyV8QZ1Nkh=s100-p-k-no-mo" + } + ] + }, + { + "name": "places/ChIJD3uTd9hx5kcR1IQvGfr8dbk/photos/AelY_CvCkiWjGUCo_rGTxwmDkN2uJq6pmIoJoOUPKRjqhQ2ofUJ88w_NsMjrcq7UEpz6cJ96zu_tmE9jfAdOJf6b0JQHvyEauRKRKjAZOvmChSP5Z5WXsCz6jpu7bEacMv26CQ4-W32g_2fKA-2OS_BvOpm98rn3jEFDTx7X", + "widthPx": 4032, + "heightPx": 3024, + "authorAttributions": [ + { + "displayName": "s83karthik", + "uri": "//maps.google.com/maps/contrib/112698298149456566911", + "photoUri": "//lh3.googleusercontent.com/a-/ALV-UjVqDhwmV4mGVRMHQVAZIF-yYiAgVt7YEKFo20UzITdzlgjaFq-j=s100-p-k-no-mo" + } + ] + }, + { + "name": "places/ChIJD3uTd9hx5kcR1IQvGfr8dbk/photos/AelY_CvJS-V_n8mAHHa4magzV10kVZDVsHCxuX3uOdJ0ekOLCUgN38_bqAzwAiYDNg2FgQz6p5QxMIbUbMJPJw7KrpzLIBSHgVf4AB5_d0IKLrv449R-vZwyOLnULHTON_-IHTIaaEtiqxQ1grBO1TfOF2Kw7JsBvFnHQ2GK", + "widthPx": 4032, + "heightPx": 3024, + "authorAttributions": [ + { + "displayName": "Marko Kull", + "uri": "//maps.google.com/maps/contrib/102284672242873683732", + "photoUri": "//lh3.googleusercontent.com/a/ACg8ocIbPi933u8YTyHEsS0BKgrim5gNXKFqxQA2F8bjllONA8Ahtg=s100-p-k-no-mo" + } + ] + }, + { + "name": "places/ChIJD3uTd9hx5kcR1IQvGfr8dbk/photos/AelY_Cuq14gmltiP8RAIu98Z5frWG8Cb8fHyOi_CWg-IPiZhNgFN6cOQAFWkL8rIiyZM93_klciXsBRtx32attLCFu6HZnnqc3WmKtUYP5_MvANU6xW6tZZn8wJv2T29qk4V6gK03UQvSGwE2c1rq8z8SnALf2_Ene35mumS", + "widthPx": 4608, + "heightPx": 2184, + "authorAttributions": [ + { + "displayName": "Ivis Danai Garcia", + "uri": "//maps.google.com/maps/contrib/105917148384902625423", + "photoUri": "//lh3.googleusercontent.com/a-/ALV-UjXziibJ87A7AgFjX5Rz1o2zdvXiS3vT_DxE8dtB6bAtrVJibvh40w=s100-p-k-no-mo" + } + ] + }, + { + "name": "places/ChIJD3uTd9hx5kcR1IQvGfr8dbk/photos/AelY_CsqeEEVV9AdXDC6XUObHmpiIt8Ij1xuINTKumXXSHiffFsY4iQELHr9Tz9wWUVT5-R9Qlhb76JT-LrO5bZMm3s3xiTODK9MFd0-QjBURsFcinpyHTb7BpBsNlCWFjcbuMuoJmOfhvqxg2FzyUtql_EwnDFoST61GCBL", + "widthPx": 4160, + "heightPx": 2340, + "authorAttributions": [ + { + "displayName": "Javier Iglesias", + "uri": "//maps.google.com/maps/contrib/102526504588145019580", + "photoUri": "//lh3.googleusercontent.com/a-/ALV-UjUQsMvzkV0v_WqM6JQ8g9pAcnljyHZeBMs9QAjH5edZTIr43nTYlQ=s100-p-k-no-mo" + } + ] + }, + { + "name": "places/ChIJD3uTd9hx5kcR1IQvGfr8dbk/photos/AelY_CunxwUe2F8x8Xzaa-EiQ4ZC4HxVYukXTA_8N79joM4NKZJH5gu78A5cYxNo6OcbzuNFIZ2aR972898XIBiEOUu2r88DnCnoyrAdil4rsAGUX4WeWUa7jj_SlPYH7l7u6BimPjzvSgxa5f8gzoKX2lIuKOHYx3mI_zex", + "widthPx": 4800, + "heightPx": 2700, + "authorAttributions": [ + { + "displayName": "Tejaswi Mupparaju", + "uri": "//maps.google.com/maps/contrib/108918847443997552687", + "photoUri": "//lh3.googleusercontent.com/a-/ALV-UjV6Z6mkXQLT1Jd8z3J_zqoc9_fZUCue9yrsPYQ3jP6F7gjSw3ya=s100-p-k-no-mo" + } + ] + } + ] + }, + "ChIJLU7jZClu5kcR4PcOOO6p3I0": { + "id": "ChIJLU7jZClu5kcR4PcOOO6p3I0", + "displayName": { + "text": "Eiffel Tower", + "languageCode": "en" + }, + "name": "places/ChIJLU7jZClu5kcR4PcOOO6p3I0", + "addressComponents": [ + { + "longText": "Avenue Gustave Eiffel", + "shortText": "Av. Gustave Eiffel", + "types": ["route"], + "languageCode": "fr" + }, + { + "longText": "Paris", + "shortText": "Paris", + "types": ["locality", "political"], + "languageCode": "fr" + }, + { + "longText": "Département de Paris", + "shortText": "Département de Paris", + "types": ["administrative_area_level_2", "political"], + "languageCode": "fr" + }, + { + "longText": "Île-de-France", + "shortText": "IDF", + "types": ["administrative_area_level_1", "political"], + "languageCode": "fr" + }, + { + "longText": "France", + "shortText": "FR", + "types": ["country", "political"], + "languageCode": "en" + }, + { + "longText": "75007", + "shortText": "75007", + "types": ["postal_code"], + "languageCode": "en-US" + } + ], + "adrFormatAddress": "Av. Gustave Eiffel, 75007 Paris, France", + "shortFormattedAddress": "Eiffel Tower, Av. Gustave Eiffel, Paris", + "formattedAddress": "Av. Gustave Eiffel, 75007 Paris, France", + "location": { + "latitude": 48.858370099999995, + "longitude": 2.2944812999999997 + }, + "plusCode": { + "globalCode": "8FW4V75V+8Q", + "compoundCode": "V75V+8Q Paris, France" + }, + "types": [ + "historical_landmark", + "tourist_attraction", + "point_of_interest", + "establishment" + ], + "viewport": { + "low": { + "latitude": 48.8566814197085, + "longitude": 2.2934150999999985 + }, + "high": { + "latitude": 48.8593793802915, + "longitude": 2.296314600000002 + } + }, + "accessibilityOptions": { + "wheelchairAccessibleParking": true, + "wheelchairAccessibleEntrance": true, + "wheelchairAccessibleRestroom": true + }, + "businessStatus": "OPERATIONAL", + "googleMapsUri": "https://maps.google.com/?cid=10222232094831998944", + "iconBackgroundColor": "#13B5C7", + "iconMaskBaseUri": "https://maps.gstatic.com/mapfiles/place_api/icons/v2/generic_pinlet", + "primaryType": "historical_landmark", + "primaryTypeDisplayName": { + "text": "Historical landmark", + "languageCode": "en" + }, + "subDestinations": null, + "utcOffsetMinutes": 120, + "currentOpeningHours": { + "openNow": true, + "periods": [ + { + "open": { + "day": 0, + "hour": 9, + "minute": 0, + "date": { + "year": 2024, + "month": 8, + "day": 11 + } + }, + "close": { + "day": 1, + "hour": 0, + "minute": 0, + "date": { + "year": 2024, + "month": 8, + "day": 12 + } + } + }, + { + "open": { + "day": 1, + "hour": 9, + "minute": 0, + "date": { + "year": 2024, + "month": 8, + "day": 12 + } + }, + "close": { + "day": 2, + "hour": 0, + "minute": 0, + "date": { + "year": 2024, + "month": 8, + "day": 13 + } + } + }, + { + "open": { + "day": 2, + "hour": 9, + "minute": 0, + "date": { + "year": 2024, + "month": 8, + "day": 13 + } + }, + "close": { + "day": 3, + "hour": 0, + "minute": 0, + "date": { + "year": 2024, + "month": 8, + "day": 14 + } + } + }, + { + "open": { + "day": 3, + "hour": 9, + "minute": 0, + "date": { + "year": 2024, + "month": 8, + "day": 14 + } + }, + "close": { + "day": 3, + "hour": 23, + "minute": 59, + "truncated": true, + "date": { + "year": 2024, + "month": 8, + "day": 14 + } + } + }, + { + "open": { + "day": 4, + "hour": 9, + "minute": 0, + "date": { + "year": 2024, + "month": 8, + "day": 8 + } + }, + "close": { + "day": 5, + "hour": 0, + "minute": 0, + "date": { + "year": 2024, + "month": 8, + "day": 9 + } + } + }, + { + "open": { + "day": 5, + "hour": 9, + "minute": 0, + "date": { + "year": 2024, + "month": 8, + "day": 9 + } + }, + "close": { + "day": 6, + "hour": 0, + "minute": 0, + "date": { + "year": 2024, + "month": 8, + "day": 10 + } + } + }, + { + "open": { + "day": 6, + "hour": 9, + "minute": 0, + "date": { + "year": 2024, + "month": 8, + "day": 10 + } + }, + "close": { + "day": 0, + "hour": 0, + "minute": 0, + "date": { + "year": 2024, + "month": 8, + "day": 11 + } + } + } + ], + "weekdayDescriptions": [ + "Monday: 9:00 AM – 12:00 AM", + "Tuesday: 9:00 AM – 12:00 AM", + "Wednesday: 9:00 AM – 12:00 AM", + "Thursday: 9:00 AM – 12:00 AM", + "Friday: 9:00 AM – 12:00 AM", + "Saturday: 9:00 AM – 12:00 AM", + "Sunday: 9:00 AM – 12:00 AM" + ] + }, + "currentSecondaryOpeningHours": null, + "internationalPhoneNumber": null, + "nationalPhoneNumber": null, + "priceLevel": null, + "rating": 4.7, + "regularOpeningHours": { + "openNow": true, + "periods": [ + { + "open": { + "day": 0, + "hour": 9, + "minute": 0 + }, + "close": { + "day": 1, + "hour": 0, + "minute": 0 + } + }, + { + "open": { + "day": 1, + "hour": 9, + "minute": 0 + }, + "close": { + "day": 2, + "hour": 0, + "minute": 0 + } + }, + { + "open": { + "day": 2, + "hour": 9, + "minute": 0 + }, + "close": { + "day": 3, + "hour": 0, + "minute": 0 + } + }, + { + "open": { + "day": 3, + "hour": 9, + "minute": 0 + }, + "close": { + "day": 4, + "hour": 0, + "minute": 0 + } + }, + { + "open": { + "day": 4, + "hour": 9, + "minute": 0 + }, + "close": { + "day": 5, + "hour": 0, + "minute": 0 + } + }, + { + "open": { + "day": 5, + "hour": 9, + "minute": 0 + }, + "close": { + "day": 6, + "hour": 0, + "minute": 0 + } + }, + { + "open": { + "day": 6, + "hour": 9, + "minute": 0 + }, + "close": { + "day": 0, + "hour": 0, + "minute": 0 + } + } + ], + "weekdayDescriptions": [ + "Monday: 9:00 AM – 12:00 AM", + "Tuesday: 9:00 AM – 12:00 AM", + "Wednesday: 9:00 AM – 12:00 AM", + "Thursday: 9:00 AM – 12:00 AM", + "Friday: 9:00 AM – 12:00 AM", + "Saturday: 9:00 AM – 12:00 AM", + "Sunday: 9:00 AM – 12:00 AM" + ] + }, + "regularSecondaryOpeningHours": null, + "userRatingCount": 395157, + "websiteUri": "https://www.toureiffel.paris/fr", + "allowsDogs": null, + "curbsidePickup": null, + "delivery": null, + "dineIn": null, + "editorialSummary": { + "text": "Gustave Eiffel's iconic, wrought-iron 1889 tower, with steps and elevators to observation decks.", + "languageCode": "en" + }, + "evChargeOptions": null, + "fuelOptions": null, + "goodForChildren": true, + "goodForGroups": null, + "goodForWatchingSports": null, + "liveMusic": null, + "menuForChildren": null, + "parkingOptions": null, + "paymentOptions": null, + "outdoorSeating": null, + "reservable": null, + "restroom": null, + "reviews": [ + { + "name": "places/ChIJLU7jZClu5kcR4PcOOO6p3I0/reviews/ChZDSUhNMG9nS0VJQ0FnSUNiaDdXNExnEAE", + "relativePublishTimeDescription": "a week ago", + "rating": 5, + "text": { + "text": "I visited the Eiffel Tower to watch beach volleyball during the Paris 2024 Olympics. The vibes were absolutely fantastic, filled with excitement and energy. The view from the Eiffel Tower is incredible, offering stunning panoramic sights of Paris. The combination of the sports event and the iconic location made the experience unforgettable. Love it!", + "languageCode": "en" + }, + "originalText": { + "text": "I visited the Eiffel Tower to watch beach volleyball during the Paris 2024 Olympics. The vibes were absolutely fantastic, filled with excitement and energy. The view from the Eiffel Tower is incredible, offering stunning panoramic sights of Paris. The combination of the sports event and the iconic location made the experience unforgettable. Love it!", + "languageCode": "en" + }, + "authorAttribution": { + "displayName": "Sukhum Cheff Truengtrachitkul", + "uri": "https://www.google.com/maps/contrib/106198363291758062631/reviews", + "photoUri": "https://lh3.googleusercontent.com/a-/ALV-UjWHdXO1pIrvuy3AXKqoPuGY0FfTXgdIY8aMcvRWYjz5kQvs45_Gbw=s128-c0x00000000-cc-rp-mo-ba8" + }, + "publishTime": "2024-07-30T12:41:51Z" + }, + { + "name": "places/ChIJLU7jZClu5kcR4PcOOO6p3I0/reviews/ChZDSUhNMG9nS0VJQ0FnSUNMcnZtSFF3EAE", + "relativePublishTimeDescription": "a month ago", + "rating": 5, + "text": { + "text": "I absolutely love the twinkling Eiffel Tower and would go back again and again just to see it at night. It's such an amazing piece of architecture. The views from the top were breathtaking. We had champagne at the top, which added to our excitement, though it was very expensive. We also took a boat cruise, and the views of the Eiffel Tower from the river were amazing. Overall, experiencing the Eiffel Tower firsthand was a highlight of my trip to Paris, capturing both the city's grandeur and its timeless charm.", + "languageCode": "en" + }, + "originalText": { + "text": "I absolutely love the twinkling Eiffel Tower and would go back again and again just to see it at night. It's such an amazing piece of architecture. The views from the top were breathtaking. We had champagne at the top, which added to our excitement, though it was very expensive. We also took a boat cruise, and the views of the Eiffel Tower from the river were amazing. Overall, experiencing the Eiffel Tower firsthand was a highlight of my trip to Paris, capturing both the city's grandeur and its timeless charm.", + "languageCode": "en" + }, + "authorAttribution": { + "displayName": "Highway Video", + "uri": "https://www.google.com/maps/contrib/114783197435768198244/reviews", + "photoUri": "https://lh3.googleusercontent.com/a-/ALV-UjX2mICa0MZavJG1Dhic8xW_SnPTyzR5KFSHZByhzy0mCGUa2T0=s128-c0x00000000-cc-rp-mo-ba6" + }, + "publishTime": "2024-06-19T08:35:18Z" + }, + { + "name": "places/ChIJLU7jZClu5kcR4PcOOO6p3I0/reviews/ChZDSUhNMG9nS0VJQ0FnSUN6MGJDbkp3EAE", + "relativePublishTimeDescription": "2 months ago", + "rating": 5, + "text": { + "text": "Absolutely beautiful and breathtaking views! Highly recommend purchasing tickets ahead of time, especially if you wish to go up to the summit. You can make a line to purchase tickets the day of but it is not guaranteed you’ll get one.\n\nI purchased tickets for the early morning and it was not crowded, I was able to take my time walking the area and when we went up to the summit, it was not crowded. I loved everything about my experience and highly recommend visiting!\n\nI also recommend returning at night to see it light up!", + "languageCode": "en" + }, + "originalText": { + "text": "Absolutely beautiful and breathtaking views! Highly recommend purchasing tickets ahead of time, especially if you wish to go up to the summit. You can make a line to purchase tickets the day of but it is not guaranteed you’ll get one.\n\nI purchased tickets for the early morning and it was not crowded, I was able to take my time walking the area and when we went up to the summit, it was not crowded. I loved everything about my experience and highly recommend visiting!\n\nI also recommend returning at night to see it light up!", + "languageCode": "en" + }, + "authorAttribution": { + "displayName": "Gabriela Franco", + "uri": "https://www.google.com/maps/contrib/115358726865804377514/reviews", + "photoUri": "https://lh3.googleusercontent.com/a-/ALV-UjW24BgVMbNYeBDTRqN0ItohZ3qpuoTFrqXScSmUHQYZ9hy7CO7r=s128-c0x00000000-cc-rp-mo-ba3" + }, + "publishTime": "2024-06-02T19:33:43Z" + }, + { + "name": "places/ChIJLU7jZClu5kcR4PcOOO6p3I0/reviews/ChdDSUhNMG9nS0VJQ0FnSURyenVyWmhBRRAB", + "relativePublishTimeDescription": "3 weeks ago", + "rating": 5, + "text": { + "text": "This iconic landmark of Paris, France, absolutely leaves a breathtaking memory, like a dream come true. I have been admiring this tower since childhood through books and television, and now here I am.\n\nIf you want to take a great picture, I suggest walking further from the tower so you can capture its entirety beautifully.", + "languageCode": "en" + }, + "originalText": { + "text": "This iconic landmark of Paris, France, absolutely leaves a breathtaking memory, like a dream come true. I have been admiring this tower since childhood through books and television, and now here I am.\n\nIf you want to take a great picture, I suggest walking further from the tower so you can capture its entirety beautifully.", + "languageCode": "en" + }, + "authorAttribution": { + "displayName": "Aaron Sutanto", + "uri": "https://www.google.com/maps/contrib/106649253728903107652/reviews", + "photoUri": "https://lh3.googleusercontent.com/a/ACg8ocKRQWyW9TuF6ns2cfnae6ymrIaKDeP040L7Jp0CGWQ3n1XSfA=s128-c0x00000000-cc-rp-mo-ba5" + }, + "publishTime": "2024-07-17T14:51:08Z" + }, + { + "name": "places/ChIJLU7jZClu5kcR4PcOOO6p3I0/reviews/ChdDSUhNMG9nS0VJQ0FnSUNUN1ltdjVnRRAB", + "relativePublishTimeDescription": "a month ago", + "rating": 5, + "text": { + "text": "It was my second time here. One of the largest tourist monuments in the world. It’s beautiful. It’s imposing. It’s exciting. One of the most amazing places I’ve visited. My last time here the tower was closed because of the snow. However, and after a lot of patience I managed to get in. Only the first and second floors were available. Still, it’s a spectacular view. The sunset is beautiful here. 🫶", + "languageCode": "en" + }, + "originalText": { + "text": "It was my second time here. One of the largest tourist monuments in the world. It’s beautiful. It’s imposing. It’s exciting. One of the most amazing places I’ve visited. My last time here the tower was closed because of the snow. However, and after a lot of patience I managed to get in. Only the first and second floors were available. Still, it’s a spectacular view. The sunset is beautiful here. 🫶", + "languageCode": "en" + }, + "authorAttribution": { + "displayName": "Jeferson Kozenieski Couto", + "uri": "https://www.google.com/maps/contrib/110919111696066078666/reviews", + "photoUri": "https://lh3.googleusercontent.com/a-/ALV-UjU9GPAQuk7nt8ApQSTYX8k6r0x71hPclbjdTO4G_U6dP0k1dR__3w=s128-c0x00000000-cc-rp-mo-ba5" + }, + "publishTime": "2024-06-30T19:29:23Z" + } + ], + "servesBeer": null, + "servesBreakfast": null, + "servesBrunch": null, + "servesCocktails": null, + "servesCoffee": null, + "servesDessert": null, + "servesDinner": null, + "servesLunch": null, + "servesVegetarianFood": null, + "servesWine": null, + "takeout": null, + "generativeSummary": null, + "areaSummary": null, + "updatedAt": "2024-08-08T15:58:00.879Z", + "photos": [ + { + "name": "places/ChIJLU7jZClu5kcR4PcOOO6p3I0/photos/AelY_CsbVXWG9zyAORfE6wy4XLPXyT6qm_pYSwjxHjxidT4MV6SBmPf8iIG_itAb1cO9HfYZuvyXMiGoV90fJ-LLLKdUQMbuTgXnqD0FwMQHu_IINr7eH-TeWbil28riufxXNpwZ6azRsbb2i0-gtmfcndiS1EjOWiUiqMC7", + "widthPx": 3000, + "heightPx": 4000, + "authorAttributions": [ + { + "displayName": "MD ASRAFUL ISLAM", + "uri": "//maps.google.com/maps/contrib/100339369331653004307", + "photoUri": "//lh3.googleusercontent.com/a-/ALV-UjXARST4aNbT05-sH9lGixhPea1WfAvQv_mcvdeCI0E3NkzO1npdkw=s100-p-k-no-mo" + } + ] + }, + { + "name": "places/ChIJLU7jZClu5kcR4PcOOO6p3I0/photos/AelY_CtAC_hmRvce1T0d543K4p2rTAT1i1IlO_3x69jB07Ui6MesxB-C8MPRztXM3VDCu2Z1wWYCAImqisBy2rwhk_YvKoskgo0snxbUdo4oav1HYR1TXU9otCSEG_HtBoPRArNIbQA7IrszykGNf_3HAzXfCVMPokdGTcGH", + "widthPx": 991, + "heightPx": 718, + "authorAttributions": [ + { + "displayName": "Atharva Diwate", + "uri": "//maps.google.com/maps/contrib/102041512887696065554", + "photoUri": "//lh3.googleusercontent.com/a/ACg8ocLndLIHbaDadKNLSdSzRib_gLqPhcUXrYzDVZ5dHEUYHeiUmA=s100-p-k-no-mo" + } + ] + }, + { + "name": "places/ChIJLU7jZClu5kcR4PcOOO6p3I0/photos/AelY_CskSy1-7AFte1cmaVEV_EnRrGnDgi15QzKRor4q4rb2JmDXgv-OAT7RLkDN4d-afBlY0kM8Xgedku7AJr0IdXWDYR_OSBgxYePHxzb24MK2cP7aXHl6dZljQIPTTm7_zENUp_zRWV_b3ROhpgZvYQLNgQaILJSLp0-7", + "widthPx": 4800, + "heightPx": 2553, + "authorAttributions": [ + { + "displayName": "Liquid Rime", + "uri": "//maps.google.com/maps/contrib/115977769875500985369", + "photoUri": "//lh3.googleusercontent.com/a-/ALV-UjWHK2zGCZ6GIVmju0cuK-A2trE7jTlZZMBMKMYByrgZ3hx-XIsbVw=s100-p-k-no-mo" + } + ] + }, + { + "name": "places/ChIJLU7jZClu5kcR4PcOOO6p3I0/photos/AelY_CvMr5yfx6KTvzUFKs2PjyE-p1gd6rhrcf-Pplq7BY7ySujHSu5QtanY-b4wL6dkpYA2jV8ydKAe4S0ZAGI7BfV7SLiCj2J_j9xL_FfbMzzwm1JYTRj71FD6x5l6d3769XC9v152QUUBVucDbkoJa_Z43WEH2yGEolBb", + "widthPx": 3600, + "heightPx": 4800, + "authorAttributions": [ + { + "displayName": "Lucas Domingues", + "uri": "//maps.google.com/maps/contrib/101325938964753308214", + "photoUri": "//lh3.googleusercontent.com/a-/ALV-UjXm1TQ2YrlvGNqPB6iGa-memoF42rha8kkfB8jcDMt9W6gIr5sbDw=s100-p-k-no-mo" + } + ] + }, + { + "name": "places/ChIJLU7jZClu5kcR4PcOOO6p3I0/photos/AelY_Cvq1jI7wiBfTWq_SAsfoVVobJ1r60yPtxvXvldNUg-f7WSDHF8kieXm3QiKAbnDeFAXJVd-hX4OkiKLmYsyHI5CfLMloBUubbBRsQ0sFj7_29_lYiAxXXpLVyDOtpNBzu1SFXu9b0OZFvtsbL9dRD6NswF-C2qP_pPC", + "widthPx": 4800, + "heightPx": 3600, + "authorAttributions": [ + { + "displayName": "Nicholai Corbie", + "uri": "//maps.google.com/maps/contrib/111122274267414579459", + "photoUri": "//lh3.googleusercontent.com/a-/ALV-UjWyWwGz-G-fxcaDvFlERkSEK70bIOq7jsIH6zpoSOcFgSu__vYu1g=s100-p-k-no-mo" + } + ] + }, + { + "name": "places/ChIJLU7jZClu5kcR4PcOOO6p3I0/photos/AelY_Cv_qLsIl7JKtRFqh8IIbp2sCLyRobXRLcIN7ujkWoewi8aJBg6kpJjh3629goCoWlwnt60JqGiRhpm0wiGrkQC5lMUasZVEJ3A2GrI_YZklMwluO3rCHhJQFbWod_qkTIb-cmzxc07e56mHvA7T9rmAsz2W84JjlyP8", + "widthPx": 4032, + "heightPx": 3024, + "authorAttributions": [ + { + "displayName": "Apekshit Sharma", + "uri": "//maps.google.com/maps/contrib/110342941236971536896", + "photoUri": "//lh3.googleusercontent.com/a/ACg8ocJaSogybu_bnAK4_FvlTHdNLpFEXfBJSMi5MyFPi7vNSy8QfMTi=s100-p-k-no-mo" + } + ] + }, + { + "name": "places/ChIJLU7jZClu5kcR4PcOOO6p3I0/photos/AelY_CsOld-diFGe0pmsslRLh7n1WLFe1yXHafAJJoY7UZF3efndcO-I2htk1EcyG7Tqu-GRSWzZ_xFWHSvuwF3Lfq7W3w7JASLVr12AnndfNOIx-zeXvSXOOWnUiv8SgjXu6YQwNAXB4jsp9CT0rcyxGIsPzCk28jGc37wa", + "widthPx": 2048, + "heightPx": 1536, + "authorAttributions": [ + { + "displayName": "Nayanjyoti Kalita", + "uri": "//maps.google.com/maps/contrib/102019899646541331815", + "photoUri": "//lh3.googleusercontent.com/a-/ALV-UjWBEfg2tDT4fEZKizfPfoCT5IBruGahHDOUFAc1Q4n6zWOylH1pQw=s100-p-k-no-mo" + } + ] + }, + { + "name": "places/ChIJLU7jZClu5kcR4PcOOO6p3I0/photos/AelY_Csb7e4H8LqRbbHnK0VcvF2LtKdCmbF7FmnpBzOTWlQZekWYvuRT_5eTyUF3oZRuazBT0JFCxEbrxvdlnL9_gOhDrnFsY7oAd4kHFuYx3xlU7t5-kRybFHT7DWgmowVEvqxLSPeTnZijyRW3dOnyPxfsfo20Aqg-_xfP", + "widthPx": 4032, + "heightPx": 2268, + "authorAttributions": [ + { + "displayName": "Hagen Metzler", + "uri": "//maps.google.com/maps/contrib/101760247291040695816", + "photoUri": "//lh3.googleusercontent.com/a-/ALV-UjXBLKcRzOInbyM7Qwx5wckBnrWhHOG46VytlzMKoqZb-NZ7Z_9T=s100-p-k-no-mo" + } + ] + }, + { + "name": "places/ChIJLU7jZClu5kcR4PcOOO6p3I0/photos/AelY_CvQEz-STHTtt7YFzFmFDe8JRsJb-k-D6UK0GhsvVGiax1Xrk2CXBWc8mAhnZ01Oz5sA6rDue8E1hb3ShIi5iZKgzNrZgDKlY_oe7YvFQEktD76XKOG543pWEtftz_nodFTaQXcOcG-cti1cxh_cx6kpoOwKTf_5OLNk", + "widthPx": 4800, + "heightPx": 3200, + "authorAttributions": [ + { + "displayName": "Fred Booker", + "uri": "//maps.google.com/maps/contrib/115485660678768601234", + "photoUri": "//lh3.googleusercontent.com/a-/ALV-UjW1hE7JYCY7nsQQFhykoTDtGGz6Asby8WFUlDERda9KEmhccWQ=s100-p-k-no-mo" + } + ] + }, + { + "name": "places/ChIJLU7jZClu5kcR4PcOOO6p3I0/photos/AelY_CulzHQKBDTMnK-bQOs64nrpHXDgiXtOPz4pIjf2PNHec-pH6CrOgQjtNXVu509uF2ui12uZR1FUbbFm4CBJhrt4MotZ4JPQf-E_R-lD1jm25fMSItjLt_atVIhIq3Q-Eh4vnx41SW9RSdF2o-uiKjpuJ81Ev-UyRAWi", + "widthPx": 1440, + "heightPx": 1800, + "authorAttributions": [ + { + "displayName": "Sir Baron Critique", + "uri": "//maps.google.com/maps/contrib/118109119077073007806", + "photoUri": "//lh3.googleusercontent.com/a-/ALV-UjVtSsMdkfXbCuXmSjAoQyMo4y4qhu9AbT75L8u1iv4Tcvd4OaXC=s100-p-k-no-mo" + } + ] + } + ] + }, + "ChIJATr1n-Fx5kcRjQb6q6cdQDY": { + "id": "ChIJATr1n-Fx5kcRjQb6q6cdQDY", + "displayName": { + "text": "Cathédrale Notre-Dame de Paris", + "languageCode": "en" + }, + "name": "places/ChIJATr1n-Fx5kcRjQb6q6cdQDY", + "addressComponents": [ + { + "longText": "6", + "shortText": "6", + "types": ["street_number"], + "languageCode": "en-US" + }, + { + "longText": "Parvis Notre Dame - Place Jean-Paul II", + "shortText": "Parvis Notre-Dame - Pl. Jean-Paul II", + "types": ["route"], + "languageCode": "fr" + }, + { + "longText": "Paris", + "shortText": "Paris", + "types": ["locality", "political"], + "languageCode": "fr" + }, + { + "longText": "Département de Paris", + "shortText": "Département de Paris", + "types": ["administrative_area_level_2", "political"], + "languageCode": "fr" + }, + { + "longText": "Île-de-France", + "shortText": "IDF", + "types": ["administrative_area_level_1", "political"], + "languageCode": "fr" + }, + { + "longText": "France", + "shortText": "FR", + "types": ["country", "political"], + "languageCode": "en" + }, + { + "longText": "75004", + "shortText": "75004", + "types": ["postal_code"], + "languageCode": "en-US" + } + ], + "adrFormatAddress": "6 Parvis Notre-Dame - Pl. Jean-Paul II, 75004 Paris, France", + "shortFormattedAddress": "Cathédrale Notre Dame, 6 Parvis Notre-Dame - Pl. Jean-Paul II, Paris", + "formattedAddress": "6 Parvis Notre-Dame - Pl. Jean-Paul II, 75004 Paris, France", + "location": { + "latitude": 48.85296820000001, + "longitude": 2.3499021 + }, + "plusCode": { + "globalCode": "8FW4V83X+5X", + "compoundCode": "V83X+5X Paris, France" + }, + "types": [ + "tourist_attraction", + "church", + "place_of_worship", + "point_of_interest", + "establishment" + ], + "viewport": { + "low": { + "latitude": 48.852202769708505, + "longitude": 2.3473101697084986 + }, + "high": { + "latitude": 48.854900730291504, + "longitude": 2.3500081302915024 + } + }, + "accessibilityOptions": { + "wheelchairAccessibleEntrance": true + }, + "businessStatus": "CLOSED_TEMPORARILY", + "googleMapsUri": "https://maps.google.com/?cid=3909157082539624077", + "iconBackgroundColor": "#7B9EB0", + "iconMaskBaseUri": "https://maps.gstatic.com/mapfiles/place_api/icons/v2/worship_christian_pinlet", + "primaryType": "church", + "primaryTypeDisplayName": { + "text": "Church", + "languageCode": "en" + }, + "subDestinations": null, + "utcOffsetMinutes": 120, + "currentOpeningHours": null, + "currentSecondaryOpeningHours": null, + "internationalPhoneNumber": "+33 1 42 34 56 10", + "nationalPhoneNumber": "01 42 34 56 10", + "priceLevel": null, + "rating": 4.7, + "regularOpeningHours": null, + "regularSecondaryOpeningHours": null, + "userRatingCount": 51070, + "websiteUri": "https://www.notredamedeparis.fr/", + "allowsDogs": null, + "curbsidePickup": null, + "delivery": null, + "dineIn": null, + "editorialSummary": { + "text": "Towering, 12th-century cathedral with flying buttresses & gargoyles, setting for Hugo's novel.", + "languageCode": "en" + }, + "evChargeOptions": null, + "fuelOptions": null, + "goodForChildren": null, + "goodForGroups": null, + "goodForWatchingSports": null, + "liveMusic": null, + "menuForChildren": null, + "parkingOptions": null, + "paymentOptions": null, + "outdoorSeating": null, + "reservable": null, + "restroom": null, + "reviews": [ + { + "name": "places/ChIJATr1n-Fx5kcRjQb6q6cdQDY/reviews/ChdDSUhNMG9nS0VJQ0FnSURydV91anlRRRAB", + "relativePublishTimeDescription": "2 weeks ago", + "rating": 4, + "text": { + "text": "It was in my bucket list but due to fire incident could not get the chance to visit this magnificent cathedral and history associated with it. It is near the Latin Quarter.\n\nIt is one of the most famous churches in the world with two huge towers and mighty flying buttresses and for a long time the main symbol of Paris, before the Eiffel Tower was built. An island in the Seine River, it is the historical and geographical center of Paris. The Notre-Dame Cathedral was founded in 1163 by King Louis IX (Saint Louis), and the construction took more than 150 years.\n\nCathedral is temporarily closed for at least 5-6 years. I took a couple of photos of this historic church and roamed along Seine River. I reached here at sunrise time, it was looking stunning.\n\nFact: Among other things, it was the location of Napoleon Bonaparte’s coronation in 1804 and its painting is in the Louvre Museum.", + "languageCode": "en" + }, + "originalText": { + "text": "It was in my bucket list but due to fire incident could not get the chance to visit this magnificent cathedral and history associated with it. It is near the Latin Quarter.\n\nIt is one of the most famous churches in the world with two huge towers and mighty flying buttresses and for a long time the main symbol of Paris, before the Eiffel Tower was built. An island in the Seine River, it is the historical and geographical center of Paris. The Notre-Dame Cathedral was founded in 1163 by King Louis IX (Saint Louis), and the construction took more than 150 years.\n\nCathedral is temporarily closed for at least 5-6 years. I took a couple of photos of this historic church and roamed along Seine River. I reached here at sunrise time, it was looking stunning.\n\nFact: Among other things, it was the location of Napoleon Bonaparte’s coronation in 1804 and its painting is in the Louvre Museum.", + "languageCode": "en" + }, + "authorAttribution": { + "displayName": "Sanjay Gupta", + "uri": "https://www.google.com/maps/contrib/114403170551238244802/reviews", + "photoUri": "https://lh3.googleusercontent.com/a-/ALV-UjWLv297oZB_KZfLGGJqJ7FhbIDrD9CytyxCiBNxZvhfqrOV48oE_Q=s128-c0x00000000-cc-rp-mo-ba8" + }, + "publishTime": "2024-07-20T15:49:39Z" + }, + { + "name": "places/ChIJATr1n-Fx5kcRjQb6q6cdQDY/reviews/ChZDSUhNMG9nS0VJQ0FnSUNiZ1k2RU5BEAE", + "relativePublishTimeDescription": "a week ago", + "rating": 5, + "text": { + "text": "The reason I gave 5 stars is because you can buy tickets to get to the higher levels of the cathedral. The Cathedral is magnificent like all the others, but the surrounding city is charming with ample parking and restrooms.", + "languageCode": "en" + }, + "originalText": { + "text": "The reason I gave 5 stars is because you can buy tickets to get to the higher levels of the cathedral. The Cathedral is magnificent like all the others, but the surrounding city is charming with ample parking and restrooms.", + "languageCode": "en" + }, + "authorAttribution": { + "displayName": "Zaldy !", + "uri": "https://www.google.com/maps/contrib/115286665488357651529/reviews", + "photoUri": "https://lh3.googleusercontent.com/a-/ALV-UjWYWdTPbIhZtYVbEumvcwGbwWCiAOA6rb41FDQFE-Ztito0z0iqDg=s128-c0x00000000-cc-rp-mo-ba5" + }, + "publishTime": "2024-07-28T05:05:07Z" + }, + { + "name": "places/ChIJATr1n-Fx5kcRjQb6q6cdQDY/reviews/ChdDSUhNMG9nS0VJQ0FnSUNycWE2RHBRRRAB", + "relativePublishTimeDescription": "3 weeks ago", + "rating": 5, + "text": { + "text": "Even shut down this is one of the coolest things to see in Paris. They have the whole thing blocked off with high walls so you can’t see anything at ground level. A lot of the mid level structure is also obscured by construction platforms but the walls are covered with stories and technical information about the fire and the rebuilding process including the people involved, planning etc. it’s very interesting if you’re into that sort of thing but even if you’re not, the super structure is such an imposing and dramatic piece of work that it will still leave an impression on you.", + "languageCode": "en" + }, + "originalText": { + "text": "Even shut down this is one of the coolest things to see in Paris. They have the whole thing blocked off with high walls so you can’t see anything at ground level. A lot of the mid level structure is also obscured by construction platforms but the walls are covered with stories and technical information about the fire and the rebuilding process including the people involved, planning etc. it’s very interesting if you’re into that sort of thing but even if you’re not, the super structure is such an imposing and dramatic piece of work that it will still leave an impression on you.", + "languageCode": "en" + }, + "authorAttribution": { + "displayName": "Darian Nastvogel", + "uri": "https://www.google.com/maps/contrib/115105166237107887534/reviews", + "photoUri": "https://lh3.googleusercontent.com/a-/ALV-UjW-vhbFncOBpkArSZrboaDn0XCasTGmmHnggMgT24GBDGF7YnS6hw=s128-c0x00000000-cc-rp-mo-ba3" + }, + "publishTime": "2024-07-16T00:23:02Z" + }, + { + "name": "places/ChIJATr1n-Fx5kcRjQb6q6cdQDY/reviews/ChZDSUhNMG9nS0VJQ0FnSUNqc3JYcWJBEAE", + "relativePublishTimeDescription": "3 months ago", + "rating": 4, + "text": { + "text": "A historically significant building. However, there are much more aesthetic historical buildings in France. Since there were renovations when I went, I couldn't examine it much. If you have been to Paris, it would of course be nice to visit this place too.", + "languageCode": "en" + }, + "originalText": { + "text": "A historically significant building. However, there are much more aesthetic historical buildings in France. Since there were renovations when I went, I couldn't examine it much. If you have been to Paris, it would of course be nice to visit this place too.", + "languageCode": "en" + }, + "authorAttribution": { + "displayName": "Mobile Gourmet", + "uri": "https://www.google.com/maps/contrib/116728490754545184185/reviews", + "photoUri": "https://lh3.googleusercontent.com/a-/ALV-UjWKQdxc4A-zKjaYuQaADjXkXIFxmhkjKJPlprqfa8cDmwvxxzA=s128-c0x00000000-cc-rp-mo-ba5" + }, + "publishTime": "2024-04-19T19:06:19Z" + }, + { + "name": "places/ChIJATr1n-Fx5kcRjQb6q6cdQDY/reviews/ChZDSUhNMG9nS0VJQ0FnSUREM1ByRkR3EAE", + "relativePublishTimeDescription": "4 months ago", + "rating": 5, + "text": { + "text": "Even though the Cathedral was closed, due to the work of restoration, still there are different displays all around the church that show you the inside as well as detailing the different works necessary to bring the cathedral to its original glory. The Cathedral looks really beautiful also from the outside and even though it is temporarily closed still there are a lot of people who visit it, just to get a glimpse of it.", + "languageCode": "en" + }, + "originalText": { + "text": "Even though the Cathedral was closed, due to the work of restoration, still there are different displays all around the church that show you the inside as well as detailing the different works necessary to bring the cathedral to its original glory. The Cathedral looks really beautiful also from the outside and even though it is temporarily closed still there are a lot of people who visit it, just to get a glimpse of it.", + "languageCode": "en" + }, + "authorAttribution": { + "displayName": "Febe M.", + "uri": "https://www.google.com/maps/contrib/105044235258997715880/reviews", + "photoUri": "https://lh3.googleusercontent.com/a-/ALV-UjXCGSBMueqmPIlhySxCVJQLa9Kc_l82mKjTsTWJxPYX3885z0ui=s128-c0x00000000-cc-rp-mo-ba4" + }, + "publishTime": "2024-04-06T20:45:35Z" + } + ], + "servesBeer": null, + "servesBreakfast": null, + "servesBrunch": null, + "servesCocktails": null, + "servesCoffee": null, + "servesDessert": null, + "servesDinner": null, + "servesLunch": null, + "servesVegetarianFood": null, + "servesWine": null, + "takeout": null, + "generativeSummary": null, + "areaSummary": null, + "updatedAt": "2024-08-08T17:21:22.385Z", + "photos": [ + { + "name": "places/ChIJATr1n-Fx5kcRjQb6q6cdQDY/photos/AelY_CtbQHeUbilfKKrfd7ySA80PD1ncOTPIpcYc06ltRXRNXMeLEF-UwUz9aDOma6xV71dRG_raeBdsQ1eJc15Pox1VFrjBf49e_exaQ_vD7zpoTMqgyrihNfNUQrnzainNGfZMls0f3FWjULFLW4cPlXVxngZ2_MI3ISRr", + "widthPx": 3024, + "heightPx": 4032, + "authorAttributions": [ + { + "displayName": "Jordi Puiggros", + "uri": "//maps.google.com/maps/contrib/103265519324428103048", + "photoUri": "//lh3.googleusercontent.com/a/ACg8ocL1ryHV_IprDWWijsgqDbs7avPlg6VGgEafZR4d7gL5SA9iSg=s100-p-k-no-mo" + } + ] + }, + { + "name": "places/ChIJATr1n-Fx5kcRjQb6q6cdQDY/photos/AelY_CvMlHTGsJP6ZHvh9lN8g2bG8kKCH2n0KWmVJlu179epnMgrPzIfSRxL-_xcUPPo8FIEKb1n3AEtds7N4Z9pUVk-NDT3khIMsTwMRpC17nyEAXZ3A1mMJAO5DqlL-_jQ44A_OQI3-73_m4kcLdb_knEHqFOsWWUcnufm", + "widthPx": 998, + "heightPx": 671, + "authorAttributions": [ + { + "displayName": "Aryan Tomar", + "uri": "//maps.google.com/maps/contrib/109243513813362512989", + "photoUri": "//lh3.googleusercontent.com/a-/ALV-UjXkfyp0KCZfKRmP696GKUR2guAB0esIbc3abypNQJ7-OjlgbtGz=s100-p-k-no-mo" + } + ] + }, + { + "name": "places/ChIJATr1n-Fx5kcRjQb6q6cdQDY/photos/AelY_CuXiidAaqgbsJ46tMpyriw1JJWsAmq2ftmnnKwD36NqQM71NBw2f9tUX440x8aji7tSmSIESSXxqoR5T_zdS9KfWKFzl-b3XVEh_nOk4BVPstWvogUR94TLTvYpY04GXgeu_N08Mgf3MWFO8-2izELE43Yvi4DrCVMn", + "widthPx": 4032, + "heightPx": 3024, + "authorAttributions": [ + { + "displayName": "Noel Galea", + "uri": "//maps.google.com/maps/contrib/115405258593735703039", + "photoUri": "//lh3.googleusercontent.com/a-/ALV-UjUpjB5hGEp6TJ0BOwRuOFXSmKbHOodaSP7hVU9QnKhihSHZ4DfOFQ=s100-p-k-no-mo" + } + ] + }, + { + "name": "places/ChIJATr1n-Fx5kcRjQb6q6cdQDY/photos/AelY_Ctwm7swcVRl_vB8PhI-ywWIScfluLrDZzjB4OEv93u2K7MLxCyHQ45sHFDikzfvdOgVZ2VEVmra71tCE_YtxKbKYEKpgj2K6PEoMcUiP4TFxN8JxgL5oxy_o7nhGfi2ZLtGmqYKHHUnDKiHgVKC8nlSQskM2eyq1Ak6", + "widthPx": 668, + "heightPx": 1000, + "authorAttributions": [ + { + "displayName": "Sam", + "uri": "//maps.google.com/maps/contrib/105208138820992444252", + "photoUri": "//lh3.googleusercontent.com/a-/ALV-UjW63DXmvv_8npGYAkHFbGPlSO3L8vS90jPqX8GFMENqOw6zDdE=s100-p-k-no-mo" + } + ] + }, + { + "name": "places/ChIJATr1n-Fx5kcRjQb6q6cdQDY/photos/AelY_CstB7mN9mwtDr4B4tCPPHmcVEmL2s-9Th4ZIjpSzTS1Np4HA-Osu8W0nZweBYDSRlDM9XYteDrGbizmxE2KtHI9HuMq0DlqburiwAH1DLzmp3YKRZfHeit3G1d5YhGZ2VEPNDAqGc7nbTvBvabRgnMM_-brTXMP5i2k", + "widthPx": 4800, + "heightPx": 2673, + "authorAttributions": [ + { + "displayName": "Nuno Castilho", + "uri": "//maps.google.com/maps/contrib/108958687983043558617", + "photoUri": "//lh3.googleusercontent.com/a-/ALV-UjVCz6X6q3Z5ad3KlHSqRHe7oPQI51vriaVT3vW2c6j_2rv9aPA=s100-p-k-no-mo" + } + ] + }, + { + "name": "places/ChIJATr1n-Fx5kcRjQb6q6cdQDY/photos/AelY_CvhMlOt4Mw3wblElnRkcvqZkggioSDW9_9JiIG-05aeR42Qb7tzd1H7q9kRS_RHLhng-Wbnp9InDZk8NDGR4UHtASeKfofQsC4ywGfkgnB7FI2Lr128DxloNu4KD0B_FDvFX29mHh1cIjaWyIWsuxIG1I10RbZ7X-4w", + "widthPx": 2256, + "heightPx": 1496, + "authorAttributions": [ + { + "displayName": "Neeladri Das (Neel)", + "uri": "//maps.google.com/maps/contrib/112213451233329088291", + "photoUri": "//lh3.googleusercontent.com/a-/ALV-UjVeFb0n70wEi9PuQcYKymaQObs7PiK-VAG2esD1MGI-DQlI14TqkA=s100-p-k-no-mo" + } + ] + }, + { + "name": "places/ChIJATr1n-Fx5kcRjQb6q6cdQDY/photos/AelY_Ct8hlh3f1PEZlaJlGYuN6b--QG8VOd5GZ0ONos-bz7ewf3koPw2-r_sPv9p3P8JwspQjh9VSW8bNZSCI6HLB9nzxtkpcMkq7hybQZnfyDK0GMLotd_hkw2YpnumHXHlCDZVRyvX9IgVrjcvQxavaQmKgA4cYafuFdCN", + "widthPx": 2700, + "heightPx": 4800, + "authorAttributions": [ + { + "displayName": "Markus H", + "uri": "//maps.google.com/maps/contrib/113560772659664182995", + "photoUri": "//lh3.googleusercontent.com/a-/ALV-UjVdcw3AXIIq1KlS9pV1mKND9rKnbuMpMjFnaJokyoZf0osEt6zE=s100-p-k-no-mo" + } + ] + }, + { + "name": "places/ChIJATr1n-Fx5kcRjQb6q6cdQDY/photos/AelY_CsC5jQ-Mrn7jGTz6L-e_UjfoV7LwRvPqEGjwIo4WJhv_fS9R5X1ePThJBHMqifBtBorVuNLhj7I-5y0HeERrjZGKvjCuSXNv1-rV3NeuIJd0XhHIi9cU9T-o6J5Y9Fpo_OnDBu3chO9Vr2S3A82oDDTe2Qvs4TgvKhL", + "widthPx": 2656, + "heightPx": 3984, + "authorAttributions": [ + { + "displayName": "Danica Rowan", + "uri": "//maps.google.com/maps/contrib/118385964732407479413", + "photoUri": "//lh3.googleusercontent.com/a-/ALV-UjWIzHMLm1uddGHxApr7-7PuD_k_o1FgwSzp7mOx_U2tMSM6rhFHWA=s100-p-k-no-mo" + } + ] + }, + { + "name": "places/ChIJATr1n-Fx5kcRjQb6q6cdQDY/photos/AelY_Cv32RPs2Jod2GhyMcWCMpHIpUAxM625cyK9dOdhAdUO0CV1ZDzNSFvV_KnPPaQRLvCxBLNzXjZ1s8EgJb1qyh5qlSAonHdgrTXXF7Uik-1cHF2sdup_tz1G-UpDOaW9tSyc4gU-WEY4t8WGmlMVbRA44UUI2P83UhMq", + "widthPx": 3872, + "heightPx": 2592, + "authorAttributions": [ + { + "displayName": "Taál István", + "uri": "//maps.google.com/maps/contrib/108293419841775167428", + "photoUri": "//lh3.googleusercontent.com/a-/ALV-UjVfOYo7IJ-JJpealCk5o1AhbQxs0sAiRSkC6tbhiKIyLelGbzNU=s100-p-k-no-mo" + } + ] + }, + { + "name": "places/ChIJATr1n-Fx5kcRjQb6q6cdQDY/photos/AelY_Ct6iEYek41d2aP81e0uQDbRlhqA90o7rvz_FG7LVmneCdqa_RfkTxxHT-w8sur04M0oQIzpKIL_yv7qvWt-w24hxlZcosbGlVu6Sqh-rhNwO9ZSJqOP42uj-fDqs7ctxfKiMAl_yK5wXvZSkBmhT8r3hfq5HJJGO08O", + "widthPx": 4800, + "heightPx": 3200, + "authorAttributions": [ + { + "displayName": "RJ KM", + "uri": "//maps.google.com/maps/contrib/113820885350806906332", + "photoUri": "//lh3.googleusercontent.com/a-/ALV-UjU2ATukIhOQF7-B25hf-IOjDw4RO4c4g71-O9F06ylAIDPl5qR7=s100-p-k-no-mo" + } + ] + } + ] + } + }, + "places": { + "ChIJD3uTd9hx5kcR1IQvGfr8dbk": { + "selectedAttributes": [], + "attributes": [] + }, + "ChIJLU7jZClu5kcR4PcOOO6p3I0": { + "selectedAttributes": [], + "attributes": [] + }, + "ChIJATr1n-Fx5kcRjQb6q6cdQDY": { + "selectedAttributes": [], + "attributes": [] + } + }, + "distance_matrix": {}, + "nearby_places": {}, + "directions": [] +} diff --git a/src/database/newtypes.json b/src/database/newtypes.json new file mode 100644 index 0000000..f3d9a9f --- /dev/null +++ b/src/database/newtypes.json @@ -0,0 +1,215 @@ +{ + "Automotive": [ + "car_dealer", + "car_rental", + "car_repair", + "car_wash", + "electric_vehicle_charging_station", + "gas_station", + "parking", + "rest_stop" + ], + "Business": ["farm"], + "Culture": ["art_gallery", "museum", "performing_arts_theater"], + "Education": [ + "school", + "secondary_school", + "university", + "library", + "preschool", + "primary_school" + ], + "Entertainment and Recreation": [ + "amusement_center", + "amusement_park", + "aquarium", + "banquet_hall", + "bowling_alley", + "casino", + "community_center", + "convention_center", + "cultural_center", + "dog_park", + "event_venue", + "hiking_area", + "historical_landmark", + "marina", + "movie_rental", + "movie_theater", + "national_park", + "night_club", + "park", + "tourist_attraction", + "visitor_center", + "wedding_venue", + "zoo" + ], + "Finance": ["accounting", "atm", "bank"], + "Food and Drink": [ + "american_restaurant", + "bakery", + "bar", + "barbecue_restaurant", + "brazilian_restaurant", + "breakfast_restaurant", + "brunch_restaurant", + "cafe", + "chinese_restaurant", + "coffee_shop", + "fast_food_restaurant", + "french_restaurant", + "greek_restaurant", + "hamburger_restaurant", + "ice_cream_shop", + "indian_restaurant", + "indonesian_restaurant", + "italian_restaurant", + "japanese_restaurant", + "korean_restaurant", + "lebanese_restaurant", + "meal_delivery", + "meal_takeaway", + "mediterranean_restaurant", + "mexican_restaurant", + "middle_eastern_restaurant", + "pizza_restaurant", + "ramen_restaurant", + "restaurant", + "sandwich_shop", + "seafood_restaurant", + "spanish_restaurant", + "steak_house", + "sushi_restaurant", + "thai_restaurant", + "turkish_restaurant", + "vegan_restaurant", + "vegetarian_restaurant", + "vietnamese_restaurant" + ], + "Geographical Areas": [ + "administrative_area_level_1", + "administrative_area_level_2", + "country", + "locality", + "postal_code", + "school_district" + ], + "Government": [ + "city_hall", + "courthouse", + "embassy", + "fire_station", + "local_government_office", + "police", + "post_office" + ], + "Health and Wellness": [ + "dental_clinic", + "dentist", + "doctor", + "drugstore", + "hospital", + "medical_lab", + "pharmacy", + "physiotherapist", + "spa" + ], + "Lodging": [ + "bed_and_breakfast", + "campground", + "camping_cabin", + "cottage", + "extended_stay_hotel", + "farmstay", + "guest_house", + "hostel", + "hotel", + "lodging", + "motel", + "private_guest_room", + "resort_hotel", + "rv_park" + ], + "Places of Worship": ["church", "hindu_temple", "mosque", "synagogue"], + "Services": [ + "barber_shop", + "beauty_salon", + "cemetery", + "child_care_agency", + "consultant", + "courier_service", + "electrician", + "florist", + "funeral_home", + "hair_care", + "hair_salon", + "insurance_agency", + "laundry", + "lawyer", + "locksmith", + "moving_company", + "painter", + "plumber", + "real_estate_agency", + "roofing_contractor", + "storage", + "tailor", + "telecommunications_service_provider", + "travel_agency", + "veterinary_care" + ], + "Shopping": [ + "auto_parts_store", + "bicycle_store", + "book_store", + "cell_phone_store", + "clothing_store", + "convenience_store", + "department_store", + "discount_store", + "electronics_store", + "furniture_store", + "gift_shop", + "grocery_store", + "hardware_store", + "home_goods_store", + "home_improvement_store", + "jewelry_store", + "liquor_store", + "market", + "pet_store", + "shoe_store", + "shopping_mall", + "sporting_goods_store", + "store", + "supermarket", + "wholesaler" + ], + "Sports": [ + "athletic_field", + "fitness_center", + "golf_course", + "gym", + "playground", + "ski_resort", + "sports_club", + "sports_complex", + "stadium", + "swimming_pool" + ], + "Transportation": [ + "airport", + "bus_station", + "bus_stop", + "ferry_terminal", + "heliport", + "light_rail_station", + "park_and_ride", + "subway_station", + "taxi_stand", + "train_station", + "transit_depot", + "transit_station", + "truck_stop" + ] +} diff --git a/src/database/textualFields.json b/src/database/textualFields.json new file mode 100644 index 0000000..4d0d8f3 --- /dev/null +++ b/src/database/textualFields.json @@ -0,0 +1,54 @@ +[ + "location", + "shortFormattedAddress", + "accessibilityOptions", + "businessStatus", + "googleMapsUri", + "iconBackgroundColor", + "iconMaskBaseUri", + "primaryType", + "primaryTypeDisplayName", + "subDestinations", + "utcOffsetMinutes", + "currentOpeningHours", + "currentSecondaryOpeningHours", + "internationalPhoneNumber", + "nationalPhoneNumber", + "priceLevel", + "rating", + "regularOpeningHours", + "regularSecondaryOpeningHours", + "userRatingCount", + "websiteUri", + "allowsDogs", + "curbsidePickup", + "delivery", + "dineIn", + "editorialSummary", + "evChargeOptions", + "fuelOptions", + "goodForChildren", + "goodForGroups", + "goodForWatchingSports", + "liveMusic", + "menuForChildren", + "parkingOptions", + "paymentOptions", + "outdoorSeating", + "reservable", + "restroom", + "reviews", + "servesBeer", + "servesBreakfast", + "servesBrunch", + "servesCocktails", + "servesCoffee", + "servesDessert", + "servesDinner", + "servesLunch", + "servesVegetarianFood", + "servesWine", + "takeout", + "generativeSummary", + "areaSummary" +] diff --git a/src/services/contextGeneratorService.js b/src/services/contextGeneratorService.js index 9c68cb1..17e28e1 100644 --- a/src/services/contextGeneratorService.js +++ b/src/services/contextGeneratorService.js @@ -7,48 +7,37 @@ const placeToContext = (place_id, selectedPlacesMap, savedPlacesMap) => { let attributes = selectedPlacesMap[place_id].selectedAttributes; let text = ""; - if ( - attributes.includes("formatted_address") || - (attributes.includes("geometry") && place.geometry?.location) - ) { - const lat = - typeof place.geometry?.location.lat === "function" - ? place.geometry?.location.lat() - : place.geometry?.location.lat; - const lng = - typeof place.geometry?.location.lng === "function" - ? place.geometry?.location.lng() - : place.geometry?.location.lng; - text += `- Location: ${ - attributes.includes("formatted_address") - ? place.formatted_address + if (attributes.includes("formatted_address")) { + text += `- Location: ${place.shortFormattedAddress}${ + attributes.includes("location") + ? " (" + + place.location.latitude + + ", " + + place.location.longitude + + ")" : "" - }${ - attributes.includes("geometry") ? " (" + lat + ", " + lng + ")" : "" }.\n`; } - if (attributes.includes("opening_hours")) { - text += `- Opening hours: ${place.opening_hours.weekday_text.join( + if (attributes.includes("regularOpeningHours")) { + text += `- Opening hours: ${place.regularOpeningHours.weekdayDescriptions.join( ", " )}.\n`; } if (attributes.includes("rating")) { text += `- Rating: ${place.rating}. ${ place.user_ratings_total - ? "(Total " + place.user_ratings_total + " ratings)" + ? "(Total " + place.userRatingCount + " ratings)" : "" }\n`; } - if (attributes.includes("price_level")) { + if (attributes.includes("priceLevel")) { // - 0 Free // - 1 Inexpensive // - 2 Moderate // - 3 Expensive // - 4 Very Expensive // Convert price level from number to string - - let priceLevel = ""; const priceMap = [ "Free", "Inexpensive", @@ -56,45 +45,38 @@ const placeToContext = (place_id, selectedPlacesMap, savedPlacesMap) => { "Expensive", "Very Expensive", ]; - - text += `- Price Level: ${priceMap[place.price_level]}.\n`; + text += `- place.priceLevel.\n`; } - if (attributes.includes("delivery")) { text += place.delivery ? "- Delivery Available.\n" : "- Delivery Not Available.\n"; } - - if (attributes.includes("dine_in")) { - text += place.dine_in + if (attributes.includes("dineIn")) { + text += place.dineIn ? "- Dine In Available.\n" : "- Dine In Not Available.\n"; } - if (attributes.includes("takeaway")) { text += place.takeaway ? "- Takeaway Available.\n" : "- Takeaway Not Available.\n"; } - if (attributes.includes("reservable")) { text += place.reservable ? "- Reservable.\n" : "- Not Reservable.\n"; } - - if (attributes.includes("wheelchair_accessible_entrance")) { - text += place.wheelchair_accessible_entrance + if (attributes.includes("accessibilityOptions")) { + text += place.accessibilityOptions.wheelchairAccessibleEntrance ? "- Wheelchair Accessible Entrance.\n" : "- Not Wheelchair Accessible Entrance.\n"; } - if (attributes.includes("reviews")) { text += `- Reviews: \n${place.reviews .map((review, index) => { // console.log(review.text); - return ` ${index + 1}. ${review.author_name} (Rating: ${ - review.rating - }): ${review.text}\n`; + return ` ${index + 1}. ${ + review.authorAttribution.displayName + } (Rating: ${review.rating}): ${review.text.text}\n`; }) .join("")} `; // Use .join('') to concatenate without commas } @@ -115,8 +97,9 @@ const ContextGeneratorService = { newContext += "\n"; } newContext += - `Information of ${savedPlacesMap[place_id]?.name}:\n` + - text; + `Information of ${savedPlacesMap[place_id]?.displayName.text}:\n` + + text + + "\n"; } } return newContext; @@ -173,21 +156,17 @@ const ContextGeneratorService = { newContext += "\n"; } newContext += `Nearby ${Pluralize( - e.type === "any" ? e.keyword : convertFromSnake(e.type) + convertFromSnake(e.type) )} of ${ // selectedPlacesMap[place_id].alias || - savedPlacesMap[place_id]?.name - } are (${ - e.rankBy === "distance" - ? "sorted by distance in ascending order" - : "in " + e.radius + " m radius" - }):\n`; + savedPlacesMap[place_id]?.displayName.text + } are (${"sorted by " + e.rankBy + " in ascending order"}):\n`; let counter = 1; e.places.forEach((near_place) => { if (near_place.selected) { newContext += `${counter}. ${ - savedPlacesMap[near_place.place_id]?.name || - near_place.name + savedPlacesMap[near_place.place_id]?.displayName + .text || near_place.name } (${ near_place.formatted_address || savedPlacesMap[near_place.place_id]?.vicinity @@ -208,18 +187,18 @@ const ContextGeneratorService = { } newContext += `Travel time from ${ // selectedPlacesMap[from_id].alias || - savedPlacesMap[from_id]?.name + savedPlacesMap[from_id]?.displayName.text } to ${ // selectedPlacesMap[to_id].alias || - savedPlacesMap[to_id]?.name + savedPlacesMap[to_id]?.displayName.text } is:\n`; Object.keys(distanceMatrix[from_id][to_id]).forEach((mode) => { newContext += `- ${ mode.toLowerCase() === "transit" ? "By public transport" - : mode.toLowerCase() === "walking" + : mode.toLowerCase() === "walk" ? "On foot" - : mode.toLowerCase() === "driving" + : mode.toLowerCase() === "drive" ? "By car" : "By cycle" }: ${distanceMatrix[from_id][to_id][mode].duration} (${ @@ -232,6 +211,44 @@ const ContextGeneratorService = { }, getDirectionContext: (directionInformation, savedPlacesMap) => { let newContext = ""; + + directionInformation.forEach((direction) => { + if (newContext.length > 0) { + newContext += "\n"; + } + newContext += `There are ${ + direction.routes.length + } routes from ${ + savedPlacesMap[direction.origin]?.displayName.text + } to ${ + savedPlacesMap[direction.destination]?.displayName.text + } by ${ + direction.travelMode.toLowerCase() === "transit" + ? "public transport" + : direction.travelMode.toLowerCase() === "walking" || + direction.travelMode.toLowerCase() === "walk" + ? "foot" + : direction.travelMode.toLowerCase() === "driving" || + direction.travelMode.toLowerCase() === "drive" + ? "car" + : "cycle" + }. They are:\n`; + + direction.routes.forEach((route, index) => { + newContext += `${index + 1}. Via ${route.label} | ${ + route.duration + } | ${route.distance}\n`; + if (direction.showSteps) { + route.legs.forEach((leg) => + leg.steps.map((step) => { + newContext += ` - ${step}\n`; + }) + ); + } + }); + }); + return newContext; + Object.keys(directionInformation).forEach((from_id) => { Object.keys(directionInformation[from_id]).forEach((to_id) => { Object.keys(directionInformation[from_id][to_id]).forEach( diff --git a/src/services/utils.js b/src/services/utils.js index 5a37983..18c4adf 100644 --- a/src/services/utils.js +++ b/src/services/utils.js @@ -6,32 +6,34 @@ import { } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; - export function convertFromSnake(text) { - return text - .replace(/_/g, " ") // Replace underscores with spaces - .split(" ") // Split the string into an array of words - .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) // Capitalize the first letter of each word - .join(" "); - } +export function convertFromSnake(text) { + return text + .replace(/_/g, " ") // Replace underscores with spaces + .split(" ") // Split the string into an array of words + .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) // Capitalize the first letter of each word + .join(" "); +} export function convertTravelModeToLabel(mode) { - return mode === "walking" + return mode === "WALK" ? "Walking" - : mode === "driving" + : mode === "DRIVE" ? "Driving" - : mode === "bicycling" + : mode === "BICYCLE" ? "Bicycling" - : "Public transport"; + : mode === "TRANSIT" + ? "Transit" + : "Unknown"; } export function convertTravelModeToIcon(mode) { - return mode === "walking" ? ( + return mode === "WALK" ? ( - ) : mode === "driving" ? ( + ) : mode === "DRIVE" ? ( - ) : mode === "bicycling" ? ( + ) : mode === "BICYCLE" ? ( - ) : ( + ) : mode === "TRANSIT" ? ( - ); + ) : null; } From 3e1838cd30827c9afe77fff900128e262f3efc4a Mon Sep 17 00:00:00 2001 From: Mahir Labib Dihan Date: Fri, 9 Aug 2024 04:47:55 +0600 Subject: [PATCH 003/253] Query Shown --- src/api/queryApi.js | 8 ++- src/components/Cards/QueryCard.jsx | 67 ++++++++++++++------- src/components/Forms/QuestionAnswerForm.jsx | 8 ++- src/components/SaveQuery.jsx | 16 +++-- src/contexts/AppContext.jsx | 5 +- 5 files changed, 71 insertions(+), 33 deletions(-) diff --git a/src/api/queryApi.js b/src/api/queryApi.js index deb3975..ccebc7c 100644 --- a/src/api/queryApi.js +++ b/src/api/queryApi.js @@ -1,8 +1,12 @@ import Api from "./base"; class QueryApi extends Api { - getQueries = async (query) => { - return await this.get("/queries", query); + getQueries = async () => { + return await this.get("/queries"); + }; + getNewQueries = async () => { + console.log("getNewQueries"); + return await this.get("/queries/new"); }; getQuery = async (id) => { return await this.get(`/queries/${id}`); diff --git a/src/components/Cards/QueryCard.jsx b/src/components/Cards/QueryCard.jsx index 46b4f10..a5eae23 100644 --- a/src/components/Cards/QueryCard.jsx +++ b/src/components/Cards/QueryCard.jsx @@ -39,7 +39,7 @@ export default function QueryCard({ entry, onEdit, isPersonal, mode, index }) { const { setQueries } = useContext(AppContext); const { isAuthenticated } = useAuth(); const [expanded, setExpanded] = useState(index === 0); - const [category, setCategory] = useState(entry.classification); + // const [category, setCategory] = useState(entry.classification); const handleDelete = async () => { if (isAuthenticated) { @@ -56,18 +56,18 @@ export default function QueryCard({ entry, onEdit, isPersonal, mode, index }) { } }; - useEffect(() => { - const invalid = entry.evaluation?.find( - (e) => - e.model !== "mistralai/Mixtral-8x7B-Instruct-v0.1" && - e.verdict === "invalid" - ); - if (invalid) { - setFlag(true); - } else { - setFlag(entry.human.answer === 0); - } - }, [entry]); + // useEffect(() => { + // const invalid = entry.evaluation?.find( + // (e) => + // e.model !== "mistralai/Mixtral-8x7B-Instruct-v0.1" && + // e.verdict === "invalid" + // ); + // if (invalid) { + // setFlag(true); + // } else { + // setFlag(entry.human.answer === 0); + // } + // }, [entry]); const toggleAccordion = () => setExpanded(!expanded); @@ -117,7 +117,7 @@ export default function QueryCard({ entry, onEdit, isPersonal, mode, index }) { #{entry.id} - + {/* {entry.classification .split(",") .map((label, index) => ( @@ -127,7 +127,7 @@ export default function QueryCard({ entry, onEdit, isPersonal, mode, index }) { color="primary" /> ))} - + */} {/*

@@ -144,7 +144,7 @@ export default function QueryCard({ entry, onEdit, isPersonal, mode, index }) { : "truncate" }`} > - {entry.question} + {entry.name}

{flag && ( @@ -182,11 +182,34 @@ export default function QueryCard({ entry, onEdit, isPersonal, mode, index }) {
- - - {!isPersonal && mode !== "explore" && ( +
+ {entry.questions.map((question, index) => ( +
+ + Question {index + 1}: + + + +
+ {question.title} +
+
+ +
+
+ ))} +
+ + {/* */} + {/* */} + {/* {!isPersonal && mode !== "explore" && ( - )} + )} */} {/* Only creator can edit his question */} {(entry.username === getUserName() || @@ -194,7 +217,7 @@ export default function QueryCard({ entry, onEdit, isPersonal, mode, index }) {
{getUserName() === "admin" && (
- ))} - + */} -
- )} - - {mode === "explore" ? ( - <> - - - ) : ( - <> - - - - - )} +
)} + + {mode === "explore" ? ( + <> + + + ) : ( + <> + + + + + )} )} diff --git a/src/components/InputFields/OptionsEditor.jsx b/src/components/InputFields/OptionsEditor.jsx index 4104c9f..6bf05be 100644 --- a/src/components/InputFields/OptionsEditor.jsx +++ b/src/components/InputFields/OptionsEditor.jsx @@ -59,15 +59,38 @@ export default function OptionsEditor({ index }) { const removeOption = (i) => { if (query.questions[index].answer.correct === i) { setQuery((prev) => { - const answer = [...prev.questions[index].answer]; - answer.correct = -1; + const options = [...prev.questions[index].answer.options]; + options.splice(i, 1); return { ...prev, questions: [ ...prev.questions.slice(0, index), { ...prev.questions[index], - answer, + answer: { + ...prev.questions[index].answer, + options, + correct: -1, + }, + }, + ...prev.questions.slice(index + 1), + ], + }; + }); + } else { + setQuery((prev) => { + const options = [...prev.questions[index].answer.options]; + options.splice(i, 1); + return { + ...prev, + questions: [ + ...prev.questions.slice(0, index), + { + ...prev.questions[index], + answer: { + ...prev.questions[index].answer, + options, + }, }, ...prev.questions.slice(index + 1), ], @@ -75,24 +98,6 @@ export default function OptionsEditor({ index }) { }); } - setQuery((prev) => { - const options = [...prev.questions[index].answer.options]; - options.splice(i, 1); - return { - ...prev, - questions: [ - ...prev.questions.slice(0, index), - { - ...prev.questions[index], - answer: { - ...prev.questions[index].answer, - options, - }, - }, - ...prev.questions.slice(index + 1), - ], - }; - }); // setQuery((prev) => { // const options = [...prev.answer.options]; // options.splice(i, 1); diff --git a/src/components/InputFields/QuestionEditor.jsx b/src/components/InputFields/QuestionEditor.jsx index 84347ec..28ff44a 100644 --- a/src/components/InputFields/QuestionEditor.jsx +++ b/src/components/InputFields/QuestionEditor.jsx @@ -39,7 +39,7 @@ export default function QuestionEditor({ index }) { return ( - Question: + Question {index + 1}:
{ }; return ( - +
Save this to dataset? { />
} secondary={ - showSteps && ( -
- {route.legs.map((leg, i) => ( - <> - {route.legs.length > 1 && ( - +
+ {route.legs.map((leg, i) => ( + <> + {route.legs.length > 1 && ( +
+ { savedPlacesMap[ waypoints[i] ].displayName .text } - + { savedPlacesMap[ waypoints[ @@ -56,25 +64,37 @@ export default function RoutesList({ routes, showSteps, waypoints }) { .text } - )} + + {leg.localizedValues + .staticDuration + .text + + " (" + + leg + .localizedValues + .distance + .text + + ")"} + +
+ )} - {leg.steps.map( - (step, j) => ( -

- ) - )} - - ))} -

- ) + {showSteps && + leg.steps.map((step, j) => ( +

+ ))} + + ))} +

} primaryTypographyProps={{ noWrap: true, diff --git a/src/components/SaveQuery.jsx b/src/components/SaveQuery.jsx index a7c3f12..c7abcb9 100644 --- a/src/components/SaveQuery.jsx +++ b/src/components/SaveQuery.jsx @@ -4,12 +4,13 @@ import { Button, Typography, Box, Snackbar, TextField } from "@mui/material"; import SaveIcon from "@mui/icons-material/Save"; import DeleteIcon from "@mui/icons-material/Delete"; -const SaveQuery = ({ onSave, onDiscard }) => { +const SaveQuery = ({ prevName, onSave, onDiscard }) => { const [showSnackbar, setShowSnackbar] = useState(false); - const [queryName, setQueryName] = useState(""); - const handleSave = () => { + const [queryName, setQueryName] = useState(prevName || ""); + const handleSave = (e) => { + e.preventDefault(); onSave(queryName); - setShowSnackbar(true); + // setShowSnackbar(true); }; return ( diff --git a/src/services/contextGeneratorService.js b/src/services/contextGeneratorService.js index 17e28e1..d91b69f 100644 --- a/src/services/contextGeneratorService.js +++ b/src/services/contextGeneratorService.js @@ -98,8 +98,7 @@ const ContextGeneratorService = { } newContext += `Information of ${savedPlacesMap[place_id]?.displayName.text}:\n` + - text + - "\n"; + text; } } return newContext; @@ -216,36 +215,137 @@ const ContextGeneratorService = { if (newContext.length > 0) { newContext += "\n"; } - newContext += `There are ${ - direction.routes.length - } routes from ${ - savedPlacesMap[direction.origin]?.displayName.text - } to ${ - savedPlacesMap[direction.destination]?.displayName.text - } by ${ - direction.travelMode.toLowerCase() === "transit" - ? "public transport" - : direction.travelMode.toLowerCase() === "walking" || - direction.travelMode.toLowerCase() === "walk" - ? "foot" - : direction.travelMode.toLowerCase() === "driving" || - direction.travelMode.toLowerCase() === "drive" - ? "car" - : "cycle" - }. They are:\n`; + if (direction.intermediates.length > 0) { + if (direction.optimizeWaypointOrder) { + newContext += `If we want to start our journey from ${ + savedPlacesMap[direction.origin]?.displayName.text + } then visit ${direction.intermediates.map( + (intermediate) => + `${savedPlacesMap[intermediate]?.displayName.text},` + )} and end our journey at to ${ + savedPlacesMap[direction.destination]?.displayName.text + }, the optimized order of visit by ${ + direction.travelMode.toLowerCase() === "transit" + ? "public transport" + : direction.travelMode.toLowerCase() === + "walking" || + direction.travelMode.toLowerCase() === "walk" + ? "foot" + : direction.travelMode.toLowerCase() === + "driving" || + direction.travelMode.toLowerCase() === "drive" + ? "car" + : "cycle" + } is:\n`; + newContext += `First go to ${ + savedPlacesMap[ + direction.intermediates[ + direction.routes[0] + .optimizedIntermediateWaypointIndex[0] + ] + ]?.displayName.text + } from ${ + savedPlacesMap[direction.origin]?.displayName.text + } which takes ${ + direction.routes[0].legs[0].localizedValues + .staticDuration.text + + " (" + + direction.routes[0].legs[0].localizedValues.distance + .text + + ")" + }.\n`; - direction.routes.forEach((route, index) => { - newContext += `${index + 1}. Via ${route.label} | ${ - route.duration - } | ${route.distance}\n`; - if (direction.showSteps) { - route.legs.forEach((leg) => - leg.steps.map((step) => { - newContext += ` - ${step}\n`; - }) - ); + for (let i = 1; i < direction.intermediates.length; i++) { + newContext += `Then go to ${ + savedPlacesMap[ + direction.intermediates[ + direction.routes[0] + .optimizedIntermediateWaypointIndex[i] + ] + ]?.displayName.text + } from ${ + savedPlacesMap[ + direction.intermediates[ + direction.routes[0] + .optimizedIntermediateWaypointIndex[ + i - 1 + ] + ] + ]?.displayName.text + } which takes ${ + direction.routes[0].legs[i].localizedValues + .staticDuration.text + + " (" + + direction.routes[0].legs[i].localizedValues.distance + .text + + ")" + }.\n`; + } + + newContext += `Finally go to ${ + savedPlacesMap[direction.destination]?.displayName.text + } from ${ + savedPlacesMap[ + direction.intermediates[ + direction.routes[0] + .optimizedIntermediateWaypointIndex[ + direction.intermediates.length - 1 + ] + ] + ]?.displayName.text + } which takes ${ + direction.routes[0].legs[ + direction.routes[0].legs.length - 1 + ].localizedValues.staticDuration.text + + " (" + + direction.routes[0].legs[ + direction.routes[0].legs.length - 1 + ].localizedValues.distance.text + + ")" + }.\n`; + + newContext += `The complete route is Via ${ + direction.routes[0].label + } and total time taken is ${ + direction.routes[0].duration + + " (" + + direction.routes[0].distance + + ")" + }.\n`; + } else { } - }); + } else { + newContext += `There are ${ + direction.routes.length + } routes from ${ + savedPlacesMap[direction.origin]?.displayName.text + } to ${ + savedPlacesMap[direction.destination]?.displayName.text + } by ${ + direction.travelMode.toLowerCase() === "transit" + ? "public transport" + : direction.travelMode.toLowerCase() === "walking" || + direction.travelMode.toLowerCase() === "walk" + ? "foot" + : direction.travelMode.toLowerCase() === "driving" || + direction.travelMode.toLowerCase() === "drive" + ? "car" + : "cycle" + }. They are:\n`; + + direction.routes.forEach((route, index) => { + newContext += `${index + 1}. Via ${route.label} | ${ + route.duration + } | ${route.distance}\n`; + if (direction.showSteps) { + route.legs.forEach((leg) => + leg.steps.map((step) => { + newContext += ` - ${step}\n`; + }) + ); + } + }); + } }); return newContext; From 6cba10eb434a4f7924aadf9624a226e421cc9fc6 Mon Sep 17 00:00:00 2001 From: Mahir Labib Dihan Date: Fri, 9 Aug 2024 20:52:51 +0600 Subject: [PATCH 006/253] More update --- src/api/queryApi.js | 3 + src/components/Cards/QueryCard.jsx | 47 ++++- src/components/InputFields/OptionsEditor.jsx | 1 + src/components/InputFields/QuestionEditor.jsx | 182 +++++++++++++++++- src/services/contextGeneratorService.js | 6 +- 5 files changed, 219 insertions(+), 20 deletions(-) diff --git a/src/api/queryApi.js b/src/api/queryApi.js index d7a28b2..ef6c11a 100644 --- a/src/api/queryApi.js +++ b/src/api/queryApi.js @@ -35,6 +35,9 @@ class QueryApi extends Api { deleteQuery = async (id) => { return await this.delete(`/queries/${id}`); }; + deleteNewQuery = async (id) => { + return await this.delete(`/queries/new/${id}`); + }; getGPTContext = async (context) => { console.log(context); return await this.post("/queries/gpt/context", { content: context }); diff --git a/src/components/Cards/QueryCard.jsx b/src/components/Cards/QueryCard.jsx index 958d210..3b3bc44 100644 --- a/src/components/Cards/QueryCard.jsx +++ b/src/components/Cards/QueryCard.jsx @@ -33,17 +33,19 @@ import { FormControl, Select, MenuItem, InputLabel } from "@mui/material"; import categories from "@/database/categories.json"; import { convertFromSnake } from "@/services/utils"; import { AppContext } from "@/contexts/AppContext"; +import ContextGeneratorService from "@/services/contextGeneratorService"; export default function QueryCard({ entry, onEdit, isPersonal, mode, index }) { const [flag, setFlag] = useState(false); const { setQueries } = useContext(AppContext); const { isAuthenticated } = useAuth(); const [expanded, setExpanded] = useState(index === 0); + const [context, setContext] = useState(); // const [category, setCategory] = useState(entry.classification); const handleDelete = async () => { if (isAuthenticated) { - const res = await queryApi.deleteQuery(entry.id); + const res = await queryApi.deleteNewQuery(entry.id); if (res.success) { setQueries((prev) => prev.filter((q) => q.id !== entry.id)); showSuccess("Query deleted successfully"); @@ -84,6 +86,27 @@ export default function QueryCard({ entry, onEdit, isPersonal, mode, index }) { URL.revokeObjectURL(href); }; + useEffect(() => { + const raw = { + places: ContextGeneratorService.getPlacesContext( + entry.context_json.places, + entry.context_json.saved_places + ), + nearby: ContextGeneratorService.getNearbyContext( + entry.context_json.nearby_places, + entry.context_json.saved_places + ), + distance: ContextGeneratorService.getDistanceContext( + entry.context_json.distance_matrix, + entry.context_json.saved_places + ), + direction: ContextGeneratorService.getDirectionContext( + entry.context_json.directions, + entry.context_json.saved_places + ), + }; + setContext(ContextGeneratorService.convertContextToText(raw)); + }, []); return (
{expanded && (
- - - Context: - - - - - + {context && ( + + + Context: + + + + + + )} +
{entry.questions.map((question, index) => (
diff --git a/src/components/InputFields/OptionsEditor.jsx b/src/components/InputFields/OptionsEditor.jsx index 6bf05be..d06c233 100644 --- a/src/components/InputFields/OptionsEditor.jsx +++ b/src/components/InputFields/OptionsEditor.jsx @@ -126,6 +126,7 @@ export default function OptionsEditor({ index }) { > { + const names = Object.values(savedPlacesMap).map( + (place) => place.displayName.text + ); + setNames(names); + }, [savedPlacesMap]); + + // const names = ["Alice", "Bob", "Charlie", "David", "Eve"]; + + // const handleQuestionChange = (e) => { + // const newValue = e.target.value; + // const lastChar = newValue[e.target.selectionStart - 1]; + + // if (lastChar === "@") { + // setShowMentions(true); + // setMentionAnchorEl(e.target); + // setMentionSearch(""); + // } else if (showMentions) { + // setMentionSearch(mentionSearch + lastChar); + // } + + // setQuery((prev) => { + // const newQuery = { ...prev }; + // newQuery.questions[index].title = newValue; + // return newQuery; + // }); + // }; + + const handleQuestionChange = (e) => { + const newValue = e.target.value; + const cursorPosition = e.target.selectionStart; + const textBeforeCursor = newValue.slice(0, cursorPosition); + const lastAtSymbol = textBeforeCursor.lastIndexOf("@"); + + console.log("Cursor position:", cursorPosition); + console.log("Last @ symbol position:", lastAtSymbol); + + if (lastAtSymbol !== -1 && cursorPosition > lastAtSymbol) { + const mentionText = textBeforeCursor.slice(lastAtSymbol + 1); + setShowMentions(true); + setMentionAnchorEl(e.target); + setMentionSearch(mentionText); + setMentionStartIndex(lastAtSymbol); + console.log("Showing mentions. Search:", mentionText); + } else { + setShowMentions(false); + setMentionSearch(""); + setMentionStartIndex(-1); + console.log("Hiding mentions"); + } + + setQuery((prev) => { + const newQuery = { ...prev }; + newQuery.questions[index].title = newValue; + return newQuery; + }); + }; + + // const handleMentionSelect = (name) => { + // const currentValue = query.questions[index].title; + // const cursorPosition = inputRef.current.selectionStart; + // const textBeforeCursor = currentValue.slice(0, cursorPosition); + // const textAfterCursor = currentValue.slice(cursorPosition); + // const lastAtSymbol = textBeforeCursor.lastIndexOf("@"); + // const newValue = + // textBeforeCursor.slice(0, lastAtSymbol) + + // `@${name} ` + + // textAfterCursor; + + // setQuery((prev) => { + // const newQuery = { ...prev }; + // newQuery.questions[index].title = newValue; + // return newQuery; + // }); + + // setShowMentions(false); + // inputRef.current.focus(); + // }; + + const handleMentionSelect = (name) => { + console.log("Mention selected:"); + + const currentValue = query.questions[index].title; + const cursorPosition = inputRef.current.selectionStart; + console.log("Cursor position:", cursorPosition); + const textBeforeMention = currentValue.slice(0, mentionStartIndex); + console.log("Text before mention:", textBeforeMention); + const textAfterCursor = currentValue.slice(cursorPosition); + console.log("Text after cursor:", textAfterCursor); + const newValue = textBeforeMention + `${name} ` + textAfterCursor; + + setQuery((prev) => { + const newQuery = { ...prev }; + newQuery.questions[index].title = newValue; + return newQuery; + }); + + setShowMentions(false); + setMentionSearch(""); + setMentionStartIndex(-1); + + // Use setTimeout to ensure the DOM has updated before we try to focus and set selection + setTimeout(() => { + if (inputRef.current) { + inputRef.current.focus(); + const newCursorPosition = mentionStartIndex + name.length; // +2 for @ and space + inputRef.current.setSelectionRange( + newCursorPosition, + newCursorPosition + ); + } + }, 0); + }; + + const filteredNames = names?.filter((name) => + name.toLowerCase().includes(mentionSearch.toLowerCase()) + ); + + // useEffect(() => { + // const handleClickOutside = (event) => { + // if (inputRef.current && !inputRef.current.contains(event.target)) { + // setShowMentions(false); + // } + // }; + + // document.addEventListener("mousedown", handleClickOutside); + // return () => { + // document.removeEventListener("mousedown", handleClickOutside); + // }; + // }, []); + const handleGenerateQuestion = async () => { setIsGenerating(true); const text = ContextGeneratorService.convertContextToText(context); @@ -46,16 +190,11 @@ export default function QuestionEditor({ index }) { fullWidth placeholder="Write a question based on context..." value={query.questions[index].title} - onChange={(e) => - setQuery((prev) => { - const newQuery = { ...prev }; - newQuery.questions[index].title = e.target.value; - return newQuery; - }) - } + onChange={handleQuestionChange} multiline required minRows={4} + inputRef={inputRef} /> + {showMentions && ( + + {filteredNames.map((name) => ( + { + console.log("ListItem clicked:", name); + handleMentionSelect(name); + }} + > + + + ))} + + )}
); diff --git a/src/services/contextGeneratorService.js b/src/services/contextGeneratorService.js index d91b69f..c007c4a 100644 --- a/src/services/contextGeneratorService.js +++ b/src/services/contextGeneratorService.js @@ -219,7 +219,7 @@ const ContextGeneratorService = { if (direction.optimizeWaypointOrder) { newContext += `If we want to start our journey from ${ savedPlacesMap[direction.origin]?.displayName.text - } then visit ${direction.intermediates.map( + }, then visit ${direction.intermediates.map( (intermediate) => `${savedPlacesMap[intermediate]?.displayName.text},` )} and end our journey at to ${ @@ -400,8 +400,8 @@ const ContextGeneratorService = { context.nearby !== "" ? (text !== "" ? "\n" : "") + context.nearby : ""; - text += - context.area !== "" ? (text !== "" ? "\n" : "") + context.area : ""; + // text += + // context.area !== "" ? (text !== "" ? "\n" : "") + context.area : ""; text += context.distance !== "" ? (text !== "" ? "\n" : "") + context.distance From ee8a9997ca159954ba8a177b23f068e9cbec5c62 Mon Sep 17 00:00:00 2001 From: Mahir Labib Dihan Date: Fri, 9 Aug 2024 21:43:14 +0600 Subject: [PATCH 007/253] Fix build error --- src/app/home/answer/page.js | 1 + src/app/home/evaluation/page.js | 1 + src/app/home/explore/page.js | 1 + src/app/home/summary/page.js | 1 + 4 files changed, 4 insertions(+) diff --git a/src/app/home/answer/page.js b/src/app/home/answer/page.js index e904305..9e3b2ce 100644 --- a/src/app/home/answer/page.js +++ b/src/app/home/answer/page.js @@ -22,6 +22,7 @@ import CorrectAnswerEditor from "@/components/InputFields/CorrectAnswerEditor"; import { useRouter } from "next/navigation"; export default function ProvideAnswerPage() { + return; const { query, context, setAnswerStatus } = useContext(GlobalContext); const [answer, setAnswer] = useState(""); const [isSubmitting, setIsSubmitting] = useState(false); diff --git a/src/app/home/evaluation/page.js b/src/app/home/evaluation/page.js index a83c1e4..deee989 100644 --- a/src/app/home/evaluation/page.js +++ b/src/app/home/evaluation/page.js @@ -52,6 +52,7 @@ const llmApis = { const n_stages = Object.keys(llmApis).length + 2; export default function LiveEvaluation() { + return; const { query, initQuery, diff --git a/src/app/home/explore/page.js b/src/app/home/explore/page.js index 08842a5..d8e6fde 100644 --- a/src/app/home/explore/page.js +++ b/src/app/home/explore/page.js @@ -5,6 +5,7 @@ import { Box, Container } from "@mui/material"; import { useRouter } from "next/navigation"; export default function ExplorePage() { + return; const router = useRouter(); return ( diff --git a/src/app/home/summary/page.js b/src/app/home/summary/page.js index 5f44382..f6c2f11 100644 --- a/src/app/home/summary/page.js +++ b/src/app/home/summary/page.js @@ -94,6 +94,7 @@ const LoginPrompt = ({ onLogin }) => { ); }; export default function LiveEvaluation() { + return; const { query, initQuery, From abf771103368d1ed797abb56570b47c0045760db Mon Sep 17 00:00:00 2001 From: Mahir Labib Dihan Date: Sat, 10 Aug 2024 02:09:11 +0600 Subject: [PATCH 008/253] llm full response added --- src/api/queryApi.js | 5 +++ src/components/Cards/QueryCard.jsx | 49 +++++++++++++++++++--- src/components/Tables/LLMAnswers.jsx | 62 +++++++++++++++++++++++++--- 3 files changed, 104 insertions(+), 12 deletions(-) diff --git a/src/api/queryApi.js b/src/api/queryApi.js index ef6c11a..e37a5fe 100644 --- a/src/api/queryApi.js +++ b/src/api/queryApi.js @@ -45,6 +45,11 @@ class QueryApi extends Api { annotate = async (query_id, human) => { return await this.post("/queries/annotate/" + query_id, human); }; + submitForEvaluation = async (query_id, context) => { + return await this.post("/queries/" + query_id + "/evaluation", { + context, + }); + }; } const queryApi = new QueryApi(); diff --git a/src/components/Cards/QueryCard.jsx b/src/components/Cards/QueryCard.jsx index 3b3bc44..10c4823 100644 --- a/src/components/Cards/QueryCard.jsx +++ b/src/components/Cards/QueryCard.jsx @@ -26,7 +26,7 @@ import OptionsPreview from "../Lists/OptionsPreview"; import { getUserName } from "@/api/base"; import { GlobalContext } from "@/contexts/GlobalContext"; import queryApi from "@/api/queryApi"; -import { Delete, Save, GetApp } from "@mui/icons-material"; +import { Delete, Save, GetApp, Rule } from "@mui/icons-material"; import { useAuth } from "@/contexts/AuthContext"; import { showError, showSuccess } from "@/contexts/ToastProvider"; import { FormControl, Select, MenuItem, InputLabel } from "@mui/material"; @@ -107,6 +107,7 @@ export default function QueryCard({ entry, onEdit, isPersonal, mode, index }) { }; setContext(ContextGeneratorService.convertContextToText(raw)); }, []); + return (
- {entry.questions.map((question, index) => ( -
+ {entry.questions.map((question, i) => ( +
- Question {index + 1}: + Question {i + 1}: +
))}
{/* */} - {/* */} + {/* {!isPersonal && mode !== "explore" && ( )} */} @@ -316,12 +318,47 @@ export default function QueryCard({ entry, onEdit, isPersonal, mode, index }) { ) : ( <> - */} +
diff --git a/src/services/contextGeneratorService.js b/src/services/contextGeneratorService.js index 1b72a54..1caac74 100644 --- a/src/services/contextGeneratorService.js +++ b/src/services/contextGeneratorService.js @@ -440,11 +440,12 @@ const ContextGeneratorService = { direction.routes.forEach((route, index) => { newContext += `${index + 1}. Via ${route.label} | ${ route.duration - } | ${route.distance}\n`; + } (${route.distance})\n`; if (direction.showSteps) { route.legs.forEach((leg) => leg.steps.map((step) => { - newContext += ` - ${step}\n`; + if (step.navigationInstruction) + newContext += ` - ${step.navigationInstruction.instructions}\n`; }) ); } From 9589079a565ca7e333ff38996e187dbc03431ee4 Mon Sep 17 00:00:00 2001 From: Mahir Labib Dihan Date: Tue, 13 Aug 2024 02:43:44 +0600 Subject: [PATCH 017/253] Context Visualizer --- src/components/Box/RouteSummary.jsx | 2 +- src/components/Cards/DistanceCard.jsx | 138 ++++++++----- src/components/Cards/NearbyCard.jsx | 87 ++++++-- src/components/Cards/NewDirectionCard.jsx | 70 +++++-- src/components/Cards/PlaceCard.jsx | 83 +++++--- src/components/Cards/QueryCard.jsx | 18 ++ src/components/Grids/DirectionGrid.jsx | 21 +- src/components/Grids/DistanceGrid.jsx | 21 +- src/components/Grids/NearbyGrid.jsx | 13 +- src/components/Grids/PlacesGrid.jsx | 21 +- src/components/Lists/PoiList.jsx | 24 ++- src/components/Lists/RoutesList.jsx | 8 +- src/components/Steppers/ContextStepper.jsx | 50 ++++- src/components/Viewer/ContextVisualize.jsx | 224 +++++++++++++++++++++ 14 files changed, 628 insertions(+), 152 deletions(-) create mode 100644 src/components/Viewer/ContextVisualize.jsx diff --git a/src/components/Box/RouteSummary.jsx b/src/components/Box/RouteSummary.jsx index 70343d7..fc63264 100644 --- a/src/components/Box/RouteSummary.jsx +++ b/src/components/Box/RouteSummary.jsx @@ -9,8 +9,8 @@ export default function RouteSummary({ to_id, intermediates, optimized, + savedPlacesMap, }) { - const { savedPlacesMap } = useContext(AppContext); return ( diff --git a/src/components/Cards/DistanceCard.jsx b/src/components/Cards/DistanceCard.jsx index 117646e..93dfa81 100644 --- a/src/components/Cards/DistanceCard.jsx +++ b/src/components/Cards/DistanceCard.jsx @@ -1,4 +1,4 @@ -import React, { useContext, useState } from "react"; +import React, { useContext, useEffect, useState } from "react"; import { IconButton, CardContent, @@ -24,13 +24,17 @@ import { } from "@/services/utils"; import RouteSummary from "../Box/RouteSummary"; -function DistanceCardDetails({ from_id, to_id }) { - const { distanceMatrix, setDistanceMatrix } = useContext(GlobalContext); +function DistanceCardDetails({ + from_id, + to_id, + distanceMatrix, + setDistanceMatrix, + mode, +}) { const handleDelete = (mode) => { const newDistanceMatrix = { ...distanceMatrix, }; - delete newDistanceMatrix[from_id][to_id][mode]; if (Object.keys(newDistanceMatrix[from_id][to_id]).length === 0) delete newDistanceMatrix[from_id][to_id]; @@ -40,49 +44,59 @@ function DistanceCardDetails({ from_id, to_id }) { }; return ( - {Object.keys(distanceMatrix[from_id][to_id]).map((mode, index) => ( - - handleDelete(mode)} - size="small" - > - - - } - > - - {convertTravelModeToIcon(mode)} - - ( + + handleDelete(travelMode)} + size="small" + > + + + ) } - primaryTypographyProps={{ - noWrap: true, - }} - secondaryTypographyProps={{ - noWrap: true, - }} - /> - - {index < - Object.keys(distanceMatrix[from_id][to_id]).length - - 1 && } - - ))} + > + + {convertTravelModeToIcon(travelMode)} + + + + {index < + Object.keys(distanceMatrix[from_id][to_id]).length - + 1 && } + + ) + )} ); } -function DistanceCardSummary({ from_id, to_id, expanded }) { - const { distanceMatrix } = useContext(GlobalContext); - const { savedPlacesMap } = useContext(AppContext); +function DistanceCardSummary({ + from_id, + to_id, + expanded, + distanceMatrix, + savedPlacesMap, +}) { return ( <> - + { + setExpanded(index === 0); + }, [index]); return ( - + setExpanded(!expanded)} className="cursor-pointer" > - + - + ); diff --git a/src/components/Cards/NearbyCard.jsx b/src/components/Cards/NearbyCard.jsx index f19fd87..1964eef 100644 --- a/src/components/Cards/NearbyCard.jsx +++ b/src/components/Cards/NearbyCard.jsx @@ -1,4 +1,4 @@ -import React, { useContext, useState } from "react"; +import React, { useContext, useEffect, useState } from "react"; import { IconButton, Typography, @@ -23,24 +23,38 @@ const priceMap = { PRICE_LEVEL_VERY_EXPENSIVE: "Very Expensive", }; -function NearbyCardDetails({ index, place_id, entry }) { - const { nearbyPlacesMap, setNearbyPlacesMap } = useContext(GlobalContext); +function NearbyCardDetails({ + index, + place_id, + entry, + nearbyPlacesMap, + setNearbyPlacesMap, + mode, +}) { const handleTogglePlace = (e, poi_index) => { const newNearbyPlacesMap = { ...nearbyPlacesMap }; newNearbyPlacesMap[place_id][index].places[poi_index].selected = e.target.checked; setNearbyPlacesMap(newNearbyPlacesMap); }; - return ( - + ); } -function NearbyCardSummary({ index, place_id, entry, expanded }) { - const { selectedPlacesMap, nearbyPlacesMap, setNearbyPlacesMap } = - useContext(GlobalContext); - const { savedPlacesMap } = useContext(AppContext); - +function NearbyCardSummary({ + index, + place_id, + entry, + expanded, + mode, + nearbyPlacesMap, + setNearbyPlacesMap, + savedPlacesMap, +}) { const handleDelete = () => { const newNearbyPlacesMap = { ...nearbyPlacesMap }; newNearbyPlacesMap[place_id].splice(index, 1); @@ -48,7 +62,6 @@ function NearbyCardSummary({ index, place_id, entry, expanded }) { delete newNearbyPlacesMap[place_id]; setNearbyPlacesMap(newNearbyPlacesMap); }; - return ( <> {savedPlacesMap[place_id].displayName.text} - - - - - + {mode === "edit" && ( + + + + + + )} ); } -export default function NearbyCard({ index, place_id, entry, i }) { - const [expanded, setExpanded] = useState(i === 0); +export default function NearbyCard({ + index, + place_id, + entry, + i, + mode, + nearbyPlacesMap, + setNearbyPlacesMap, + savedPlacesMap, +}) { + const [expanded, setExpanded] = useState(false); + // useEffect(() => { + // setExpanded(i === 0); + // }, [i]); return ( @@ -141,11 +168,31 @@ export default function NearbyCard({ index, place_id, entry, i }) { onClick={() => setExpanded(!expanded)} className="cursor-pointer" > - + - + ); diff --git a/src/components/Cards/NewDirectionCard.jsx b/src/components/Cards/NewDirectionCard.jsx index 2b91634..eb78cb1 100644 --- a/src/components/Cards/NewDirectionCard.jsx +++ b/src/components/Cards/NewDirectionCard.jsx @@ -27,10 +27,13 @@ import { import RoutesList from "../Lists/RoutesList"; import RouteSummary from "../Box/RouteSummary"; -function DirectionCardDetails({ direction, index }) { - const { directionInformation, setDirectionInformation } = - useContext(GlobalContext); - +function DirectionCardDetails({ + direction, + index, + directionInformation, + setDirectionInformation, + savedPlacesMap, +}) { return ( ); @@ -87,11 +91,15 @@ const transitModeMap = { TRAIN: "Train", // RAIL: "Rail", // This is equivalent to a combination of SUBWAY, TRAIN, and LIGHT_RAIL. }; -function DirectionCardSummary({ direction, expanded, index }) { - const { directionInformation, setDirectionInformation } = - useContext(GlobalContext); - const { savedPlacesMap } = useContext(AppContext); - +function DirectionCardSummary({ + direction, + expanded, + index, + directionInformation, + setDirectionInformation, + savedPlacesMap, + mode, +}) { return ( <> direction.intermediates[i] ) : undefined, + savedPlacesMap, }} /> - { - const newDirectionMatrix = [ - ...directionInformation, - ]; - newDirectionMatrix.splice(index, 1); - setDirectionInformation(newDirectionMatrix); - }} - size="small" - > - - + {mode === "edit" && ( + { + const newDirectionMatrix = [ + ...directionInformation, + ]; + newDirectionMatrix.splice(index, 1); + setDirectionInformation(newDirectionMatrix); + }} + size="small" + > + + + )} ); } -export default function DirectionCard({ direction, index }) { +export default function DirectionCard({ + direction, + index, + directionInformation, + setDirectionInformation, + savedPlacesMap, + mode, +}) { const [expanded, setExpanded] = useState(index === 0); return ( @@ -193,6 +211,10 @@ export default function DirectionCard({ direction, index }) { direction, expanded, index, + directionInformation, + setDirectionInformation, + savedPlacesMap, + mode, }} /> @@ -202,6 +224,10 @@ export default function DirectionCard({ direction, index }) { {...{ direction, index, + directionInformation, + setDirectionInformation, + savedPlacesMap, + mode, }} /> diff --git a/src/components/Cards/PlaceCard.jsx b/src/components/Cards/PlaceCard.jsx index de03dd5..8bfdba8 100644 --- a/src/components/Cards/PlaceCard.jsx +++ b/src/components/Cards/PlaceCard.jsx @@ -19,10 +19,12 @@ import { AppContext } from "@/contexts/AppContext"; import { convertFromSnake } from "@/services/utils"; import PlaceDeleteButton from "../Buttons/PlaceDeleteButton"; -function PlaceCardSummary({ placeId, expanded }) { - const { selectedPlacesMap } = useContext(GlobalContext); - const { savedPlacesMap } = useContext(AppContext); - +function PlaceCardSummary({ + placeId, + expanded, + selectedPlacesMap, + savedPlacesMap, +}) { return ( <> @@ -55,9 +57,11 @@ function PlaceCardSummary({ placeId, expanded }) { ); } -function PlaceCardDetails({ placeId }) { - const { selectedPlacesMap, setSelectedPlacesMap } = - useContext(GlobalContext); +function PlaceCardDetails({ + placeId, + selectedPlacesMap, + setSelectedPlacesMap, +}) { const handleAttributeChange = (placeId, newAttributes) => { setSelectedPlacesMap((prev) => ({ ...prev, @@ -102,20 +106,20 @@ function PlaceCardDetails({ placeId }) { ); } -export default function PlaceCard({ placeId, index }) { +export default function PlaceCard({ + placeId, + index, + selectedPlacesMap, + setSelectedPlacesMap, + savedPlacesMap, + mode, +}) { const [expanded, setExpanded] = useState(false); - useEffect(() => { - setExpanded(index === 0); - }, [index]); + // useEffect(() => { + // setExpanded(index === 0); + // }, [index]); return ( - + setExpanded((prev) => !prev)} className="cursor-pointer" > - + - + {mode === "edit" && ( + + )} + - - - + {mode === "edit" && ( + + + + )} ); diff --git a/src/components/Cards/QueryCard.jsx b/src/components/Cards/QueryCard.jsx index 10c4823..58717ed 100644 --- a/src/components/Cards/QueryCard.jsx +++ b/src/components/Cards/QueryCard.jsx @@ -34,6 +34,7 @@ import categories from "@/database/categories.json"; import { convertFromSnake } from "@/services/utils"; import { AppContext } from "@/contexts/AppContext"; import ContextGeneratorService from "@/services/contextGeneratorService"; +import ContextVisualize from "../Viewer/ContextVisualize"; export default function QueryCard({ entry, onEdit, isPersonal, mode, index }) { const [flag, setFlag] = useState(false); @@ -211,6 +212,23 @@ export default function QueryCard({ entry, onEdit, isPersonal, mode, index }) { )} + + + + +
{entry.questions.map((question, i) => ( diff --git a/src/components/Grids/DirectionGrid.jsx b/src/components/Grids/DirectionGrid.jsx index 0e2f4d4..fd9145c 100644 --- a/src/components/Grids/DirectionGrid.jsx +++ b/src/components/Grids/DirectionGrid.jsx @@ -3,15 +3,26 @@ import { Grid } from "@mui/material"; import DirectionCard from "@/components/Cards/NewDirectionCard"; import { GlobalContext } from "@/contexts/GlobalContext"; -export default function DirectionGrid() { - const { directionInformation } = useContext(GlobalContext); - - console.log(directionInformation); +export default function DirectionGrid({ + directionInformation, + setDirectionInformation, + savedPlacesMap, + mode, +}) { return ( {directionInformation.map((direction, i) => ( - + ))} {/* {Object.keys(directionInformation).map((from_id, i) => diff --git a/src/components/Grids/DistanceGrid.jsx b/src/components/Grids/DistanceGrid.jsx index de7c311..b240cfc 100644 --- a/src/components/Grids/DistanceGrid.jsx +++ b/src/components/Grids/DistanceGrid.jsx @@ -2,9 +2,14 @@ import React, { useContext } from "react"; import { GlobalContext } from "@/contexts/GlobalContext"; import DistanceCard from "@/components/Cards/DistanceCard"; import { Grid } from "@mui/material"; +import { AppContext } from "@/contexts/AppContext"; -export default function DistanceGrid() { - const { distanceMatrix } = useContext(GlobalContext); +export default function DistanceGrid({ + distanceMatrix, + setDistanceMatrix, + savedPlacesMap, + mode, +}) { return ( {Object.keys(distanceMatrix).map((from_id, i) => @@ -16,7 +21,17 @@ export default function DistanceGrid() { md={6} key={from_id + "-" + to_id} > - + )) )} diff --git a/src/components/Grids/NearbyGrid.jsx b/src/components/Grids/NearbyGrid.jsx index 2110d69..f38fdb6 100644 --- a/src/components/Grids/NearbyGrid.jsx +++ b/src/components/Grids/NearbyGrid.jsx @@ -2,9 +2,14 @@ import React, { useContext } from "react"; import NearbyCard from "@/components/Cards/NearbyCard"; import { GlobalContext } from "@/contexts/GlobalContext"; import { Grid } from "@mui/material"; +import { AppContext } from "@/contexts/AppContext"; -export default function NearbyGrid() { - const { nearbyPlacesMap } = useContext(GlobalContext); +export default function NearbyGrid({ + mode, + nearbyPlacesMap, + setNearbyPlacesMap, + savedPlacesMap, +}) { return ( {Object.keys(nearbyPlacesMap).map((place_id, i) => @@ -16,6 +21,10 @@ export default function NearbyGrid() { index, entry, i, + savedPlacesMap, + nearbyPlacesMap, + setNearbyPlacesMap, + mode, }} /> diff --git a/src/components/Grids/PlacesGrid.jsx b/src/components/Grids/PlacesGrid.jsx index ddf5ce6..684284a 100644 --- a/src/components/Grids/PlacesGrid.jsx +++ b/src/components/Grids/PlacesGrid.jsx @@ -2,17 +2,30 @@ import React, { useContext } from "react"; import { Grid } from "@mui/material"; import { GlobalContext } from "@/contexts/GlobalContext"; import PlaceCard from "@/components/Cards/PlaceCard"; +import { AppContext } from "@/contexts/AppContext"; -export default function PlacesGrid() { - const { selectedPlacesMap } = useContext(GlobalContext); - +export default function PlacesGrid({ + selectedPlacesMap, + setSelectedPlacesMap, + savedPlacesMap, + mode, +}) { return ( {Object.keys(selectedPlacesMap) .reverse() .map((placeId, index) => ( - + ))} diff --git a/src/components/Lists/PoiList.jsx b/src/components/Lists/PoiList.jsx index 31f36a7..d9f3f36 100644 --- a/src/components/Lists/PoiList.jsx +++ b/src/components/Lists/PoiList.jsx @@ -12,18 +12,22 @@ import { } from "@mui/material"; import PlaceAddButton from "../Buttons/PlaceAddButton"; -export default function PoiList({ places, handleTogglePlace }) { +export default function PoiList({ places, handleTogglePlace, mode }) { return ( {places.map((place, index) => ( - handleTogglePlace(e, index)} - /> + {mode === "edit" && ( + + handleTogglePlace(e, index) + } + /> + )} - - - + {mode === "edit" && ( + + + + )} {index < places.length - 1 && } diff --git a/src/components/Lists/RoutesList.jsx b/src/components/Lists/RoutesList.jsx index 5b2fb32..068d4b1 100644 --- a/src/components/Lists/RoutesList.jsx +++ b/src/components/Lists/RoutesList.jsx @@ -16,8 +16,12 @@ import { } from "@mui/icons-material"; import { AppContext } from "@/contexts/AppContext"; -export default function RoutesList({ routes, showSteps, waypoints }) { - const { savedPlacesMap } = useContext(AppContext); +export default function RoutesList({ + routes, + showSteps, + waypoints, + savedPlacesMap, +}) { return ( {routes.map((route, index) => ( diff --git a/src/components/Steppers/ContextStepper.jsx b/src/components/Steppers/ContextStepper.jsx index 375fe6e..bb1ff11 100644 --- a/src/components/Steppers/ContextStepper.jsx +++ b/src/components/Steppers/ContextStepper.jsx @@ -45,6 +45,7 @@ import PlacesGrid from "@/components/Grids/PlacesGrid"; import { LoadingButton } from "@mui/lab"; import { setLoading } from "@/app/old-home/page"; import { showError } from "@/contexts/ToastProvider"; +import { AppContext } from "@/contexts/AppContext"; function ContextStep({ step, @@ -204,13 +205,18 @@ export default function ContextStepper({ }) { const { selectedPlacesMap, + setSelectedPlacesMap, nearbyPlacesMap, + setNearbyPlacesMap, poisMap, distanceMatrix, + setDistanceMatrix, directionInformation, + setDirectionInformation, context, } = useContext(GlobalContext); + const { savedPlacesMap } = useContext(AppContext); const handleNext = () => { if (activeStep === steps.length - 1) onFinish(); else setActiveStep((prevActiveStep) => prevActiveStep + 1); @@ -250,8 +256,15 @@ export default function ContextStepper({ form: , grid: Object.keys(selectedPlacesMap).length > 0 && ( <> - - + {/* */} + ), context: context.places, @@ -264,8 +277,17 @@ export default function ContextStepper({ additional: "List of places whose nearby pois are added to the context", icon: , - form: , - grid: Object.keys(nearbyPlacesMap).length > 0 && , + + grid: Object.keys(nearbyPlacesMap).length > 0 && ( + + ), context: context.nearby, }, // { @@ -285,7 +307,16 @@ export default function ContextStepper({ "List of origin - destination pairs whose fastest route is added to the context", icon: , form: , - grid: Object.keys(distanceMatrix).length > 0 && , + grid: Object.keys(distanceMatrix).length > 0 && ( + + ), context: context.distance, }, { @@ -296,7 +327,14 @@ export default function ContextStepper({ icon: , form: , grid: Object.keys(directionInformation).length > 0 && ( - + ), context: context.direction, }, diff --git a/src/components/Viewer/ContextVisualize.jsx b/src/components/Viewer/ContextVisualize.jsx new file mode 100644 index 0000000..3075ed5 --- /dev/null +++ b/src/components/Viewer/ContextVisualize.jsx @@ -0,0 +1,224 @@ +import { useContext, useEffect, useRef, useState } from "react"; +import { + Box, + Typography, + Stepper, + Step, + StepLabel, + StepContent, + Button, + Paper, + Divider, + CardContent, +} from "@mui/material"; +import MapIcon from "@mui/icons-material/Map"; +import SearchIcon from "@mui/icons-material/Search"; +import DirectionsIcon from "@mui/icons-material/Directions"; +import PlaceIcon from "@mui/icons-material/Place"; +import { GlobalContext } from "@/contexts/GlobalContext"; +import ContextPreview from "@/components/Cards/ContextPreview"; +import { + Clear, + Flag, + KeyboardArrowRight, + KeyboardDoubleArrowRight, + NextPlanTwoTone, + RemoveRedEye, + Save, + Send, + Settings, +} from "@mui/icons-material"; +import ExploreIcon from "@mui/icons-material/Explore"; +import ContextGeneratorService from "@/services/contextGeneratorService"; +import ParamsForm from "@/components/Forms/ParamsForm"; +import DirectionGrid from "@/components/Grids/DirectionGrid"; +import DirectionForm from "@/components/Forms/DirectionForm"; +import DistanceGrid from "@/components/Grids/DistanceGrid"; +import DistanceForm from "@/components/Forms/DistanceForm"; +import AreaGrid from "@/components/Grids/AreaGrid"; +import AreaForm from "@/components/Forms/AreaForm"; +import NearbyGrid from "@/components/Grids/NearbyGrid"; +import NearbyForm from "@/components/Forms/NearbyForm"; +import AutocompleteSearchBox from "@/components/InputFields/AutocompleteSearchBox"; +import MapComponent from "@/components/GoogleMap/MapComponent"; +import PlacesGrid from "@/components/Grids/PlacesGrid"; +import { LoadingButton } from "@mui/lab"; +import { setLoading } from "@/app/old-home/page"; +import { showError } from "@/contexts/ToastProvider"; + +function ContextStep({ + step, + index, + totalSteps, + activeStep, + setActiveStep, + handleNext, + handleBack, + loadExample, +}) { + const [loading, setLoading] = useState(false); + const stepRef = useRef(null); + + // useEffect(() => { + // if (index === activeStep && stepRef.current) { + // stepRef.current.scrollIntoView({ + // behavior: "smooth", + // block: "start", + // }); + // } + // }, [activeStep, index]); + return ( +
+ + {step.icon} +
+ } + onClick={() => { + if (index !== activeStep) setActiveStep(index); + }} + > + + {step.label} + + + +
+ {step.grid && ( + <> + + {/* Already added + */} + + {step.additional} + + {step.grid} + + )} + {step.context !== undefined && ( +
+ + Based on the information you add, a context will + be generated below. + + + + + +
+ )} +
+
+
+ ); +} + +export default function ContextVisualize({ + savedPlacesMap, + selectedPlacesMap, + nearbyPlacesMap, + distanceMatrix, + directionInformation, +}) { + console.log("SELECTED PLACES MAP", selectedPlacesMap); + const steps = [ + { + label: "Add Information of Places", + description: `Start by searching for a location. Type in a place name or address in the search bar below.`, + icon: , + additional: "Places you have added to the context.", + grid: Object.keys(selectedPlacesMap).length > 0 && ( + <> + {/* */} + + + ), + }, + { + label: "Search for Nearby places or Explore the area", + additional: + "List of places whose nearby pois are added to the context", + icon: , + grid: Object.keys(nearbyPlacesMap).length > 0 && ( + + ), + }, + { + label: "Get Distance Matrix", + additional: + "List of origin - destination pairs whose fastest route is added to the context", + icon: , + grid: Object.keys(distanceMatrix).length > 0 && ( + + ), + }, + { + label: "Get Alternative Routes", + additional: + "List of origin - destination pairs whose alternative routes are added to the context", + icon: , + grid: Object.keys(directionInformation).length > 0 && ( + + ), + }, + ]; + + return steps.map( + (step, index) => + step.grid && ( +
+ + {step.additional} + + {step.grid} + {/* {index < steps.length - 1 && } */} +
+ ) + ); +} From 6501075aa338b61da3767f919f7962f7bcb3b9d9 Mon Sep 17 00:00:00 2001 From: Mahir Labib Dihan Date: Tue, 13 Aug 2024 18:51:59 +0600 Subject: [PATCH 018/253] Context visualization expanded --- src/components/Cards/QueryCard.jsx | 63 ++++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 13 deletions(-) diff --git a/src/components/Cards/QueryCard.jsx b/src/components/Cards/QueryCard.jsx index 58717ed..ad617bc 100644 --- a/src/components/Cards/QueryCard.jsx +++ b/src/components/Cards/QueryCard.jsx @@ -10,6 +10,7 @@ import { Button, IconButton, Card, + Divider, } from "@mui/material"; import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; @@ -42,6 +43,7 @@ export default function QueryCard({ entry, onEdit, isPersonal, mode, index }) { const { isAuthenticated } = useAuth(); const [expanded, setExpanded] = useState(index === 0); const [context, setContext] = useState(); + const [contextExpanded, setContextExpanded] = useState(false); // const [category, setCategory] = useState(entry.classification); const handleDelete = async () => { @@ -214,19 +216,54 @@ export default function QueryCard({ entry, onEdit, isPersonal, mode, index }) { )} - +
+

+ {contextExpanded + ? "Hide Context" + : "Visualize Context"} +

+ + setContextExpanded((prev) => !prev) + } + sx={{ + transform: contextExpanded + ? "rotate(180deg)" + : "rotate(0deg)", + transition: "0.3s", + }} + > + + +
+ + {contextExpanded && ( + <> + + + + )}
From 342019808c3d926137fd89b3d28c70832e2e2a0d Mon Sep 17 00:00:00 2001 From: Mahir Labib Dihan Date: Wed, 14 Aug 2024 03:03:21 +0600 Subject: [PATCH 019/253] Context expansion update --- src/components/Cards/QueryCard.jsx | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/components/Cards/QueryCard.jsx b/src/components/Cards/QueryCard.jsx index ad617bc..8c76fea 100644 --- a/src/components/Cards/QueryCard.jsx +++ b/src/components/Cards/QueryCard.jsx @@ -41,11 +41,15 @@ export default function QueryCard({ entry, onEdit, isPersonal, mode, index }) { const [flag, setFlag] = useState(false); const { setQueries } = useContext(AppContext); const { isAuthenticated } = useAuth(); - const [expanded, setExpanded] = useState(index === 0); + const [expanded, setExpanded] = useState(false); const [context, setContext] = useState(); const [contextExpanded, setContextExpanded] = useState(false); // const [category, setCategory] = useState(entry.classification); + // useEffect(() => { + // setExpanded(index === 0); + // }, [index]); + const handleDelete = async () => { if (isAuthenticated) { const res = await queryApi.deleteNewQuery(entry.id); @@ -216,7 +220,12 @@ export default function QueryCard({ entry, onEdit, isPersonal, mode, index }) { )} -
+
+ setContextExpanded((prev) => !prev) + } + >

{contextExpanded ? "Hide Context" @@ -224,9 +233,7 @@ export default function QueryCard({ entry, onEdit, isPersonal, mode, index }) {

- setContextExpanded((prev) => !prev) - } + sx={{ transform: contextExpanded ? "rotate(180deg)" From f3c13758ee4aa7010df6f2b7f2325c9cd63319cf Mon Sep 17 00:00:00 2001 From: Mahir Labib Dihan Date: Wed, 14 Aug 2024 03:17:20 +0600 Subject: [PATCH 020/253] old bug fix --- src/app/old-home/ContextGenerator.jsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/app/old-home/ContextGenerator.jsx b/src/app/old-home/ContextGenerator.jsx index fd3e7d5..2bfdef4 100644 --- a/src/app/old-home/ContextGenerator.jsx +++ b/src/app/old-home/ContextGenerator.jsx @@ -72,7 +72,7 @@ export default function ContextGenerator({ Object.keys(distanceMatrix).forEach((from_id) => { Object.keys(distanceMatrix[from_id]).forEach((to_id) => { Object.keys(distanceMatrix[from_id][to_id]).forEach((mode) => { - if (mode === "transit") { + if (mode.toLowerCase() === "transit") { newContext.push( `Distance from ${ // selectedPlacesMap[from_id].alias || @@ -86,7 +86,7 @@ export default function ContextGenerator({ distanceMatrix[from_id][to_id][mode].duration }).` ); - } else if (mode === "driving") { + } else if (mode.toLowerCase() === "driving") { newContext.push( `Distance from ${ // selectedPlacesMap[from_id].alias || @@ -100,7 +100,7 @@ export default function ContextGenerator({ distanceMatrix[from_id][to_id][mode].duration }).` ); - } else if (mode === "bicycling") { + } else if (mode.toLowerCase() === "bicycling") { newContext.push( `Distance from ${ // selectedPlacesMap[from_id].alias || @@ -114,7 +114,7 @@ export default function ContextGenerator({ distanceMatrix[from_id][to_id][mode].duration }).` ); - } else if (mode === "walking") { + } else if (mode.toLowerCase() === "walking") { newContext.push( `Distance from ${ // selectedPlacesMap[from_id].alias || @@ -137,7 +137,7 @@ export default function ContextGenerator({ Object.keys(directionInformation[from_id]).forEach((to_id) => { Object.keys(directionInformation[from_id][to_id]).forEach( (mode) => { - if (mode === "transit") { + if (mode.toLowerCase() === "transit") { newContext.push( `There are ${ directionInformation[from_id][to_id][mode] @@ -150,7 +150,7 @@ export default function ContextGenerator({ savedPlacesMap[to_id].name } by public transport. They are:` ); - } else if (mode === "driving") { + } else if (mode.toLowerCase() === "driving") { newContext.push( `There are ${ directionInformation[from_id][to_id][mode] @@ -163,7 +163,7 @@ export default function ContextGenerator({ savedPlacesMap[to_id].name } by car. They are:` ); - } else if (mode === "bicycling") { + } else if (mode.toLowerCase() === "bicycling") { newContext.push( `There are ${ directionInformation[from_id][to_id][mode] @@ -176,7 +176,7 @@ export default function ContextGenerator({ savedPlacesMap[to_id].name } by cycle. They are:` ); - } else if (mode === "walking") { + } else if (mode.toLowerCase() === "walking") { newContext.push( `There are ${ directionInformation[from_id][to_id][mode] From 5a0b53bd74b120695be28deb5acc0e04752ee811 Mon Sep 17 00:00:00 2001 From: Mahir Labib Dihan Date: Wed, 14 Aug 2024 15:15:35 +0600 Subject: [PATCH 021/253] serves attributes added --- src/services/contextGeneratorService.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/services/contextGeneratorService.js b/src/services/contextGeneratorService.js index 1caac74..20b0fbe 100644 --- a/src/services/contextGeneratorService.js +++ b/src/services/contextGeneratorService.js @@ -72,6 +72,23 @@ const placeToContext = (place_id, selectedPlacesMap, savedPlacesMap) => { if (attributes.includes("reservable")) { text += place.reservable ? "- Reservable.\n" : "- Not Reservable.\n"; } + + if (attributes.includes("servesBreakfast")) { + text += place.servesBreakfast + ? "- Serves Breakfast.\n" + : "- Does Not Serve Breakfast.\n"; + } + if (attributes.includes("servesLunch")) { + text += place.servesLunch + ? "- Serves Lunch.\n" + : "- Does Not Serve Lunch.\n"; + } + + if (attributes.includes("servesDinner")) { + text += place.servesDinner + ? "- Serves Dinner.\n" + : "- Does Not Serve Dinner.\n"; + } if (attributes.includes("accessibilityOptions")) { text += place.accessibilityOptions.wheelchairAccessibleEntrance ? "- Wheelchair Accessible Entrance.\n" From 2c3d4e3b3523ceee821c089cc5f8113c9f3cce5a Mon Sep 17 00:00:00 2001 From: Mahir Labib Dihan Date: Wed, 14 Aug 2024 16:04:27 +0600 Subject: [PATCH 022/253] phone added --- src/services/contextGeneratorService.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/services/contextGeneratorService.js b/src/services/contextGeneratorService.js index 20b0fbe..ebc1b6f 100644 --- a/src/services/contextGeneratorService.js +++ b/src/services/contextGeneratorService.js @@ -25,6 +25,11 @@ const placeToContext = (place_id, selectedPlacesMap, savedPlacesMap) => { : "" }.\n`; } + + if (attributes.includes("internationalPhoneNumber")) { + text += `- Phone Number: ${place.internationalPhoneNumber}.\n`; + } + if (attributes.includes("regularOpeningHours")) { text += `- Opening hours: ${place.regularOpeningHours.weekdayDescriptions.join( ", " From 7b0fa698669baabe39e7aeacada5c13fe2838428 Mon Sep 17 00:00:00 2001 From: Mahir Labib Dihan Date: Wed, 14 Aug 2024 16:04:36 +0600 Subject: [PATCH 023/253] evaluation fixed --- src/components/Tables/LLMAnswers.jsx | 71 +++++++++++++++------------- 1 file changed, 39 insertions(+), 32 deletions(-) diff --git a/src/components/Tables/LLMAnswers.jsx b/src/components/Tables/LLMAnswers.jsx index 8563839..a0ada95 100644 --- a/src/components/Tables/LLMAnswers.jsx +++ b/src/components/Tables/LLMAnswers.jsx @@ -28,9 +28,10 @@ const extractSubstringInParentheses = (s) => { export default function LLMAnswers({ entry, index }) { console.log(entry.evaluation); const results = - entry.evaluation?.length > 0 && + entry.evaluation?.length && entry.evaluation.map((e) => { const responses = e.responses; + if (responses.length < index + 1) return null; const response = responses[index]; const option = parseInt(extract(response)); const model = e.model; @@ -99,38 +100,44 @@ export default function LLMAnswers({ entry, index }) { - {results.map((llmAnswer, index) => ( - - {llmAnswer.model} - - {llmAnswer.explanation} - - - Option {llmAnswer.option} - + {results?.map( + (llmAnswer, index) => + llmAnswer && ( + + + {llmAnswer.model} + + + {llmAnswer.explanation} + + + Option {llmAnswer.option} + - - {llmAnswer.verdict === "right" ? ( - - ) : ( - - )} - - - ))} + + {llmAnswer.verdict === + "right" ? ( + + ) : ( + + )} + + + ) + )} From 2c40b566698d98316ec10d43f7ad2a9f821939b4 Mon Sep 17 00:00:00 2001 From: Mahir Labib Dihan Date: Wed, 14 Aug 2024 16:04:46 +0600 Subject: [PATCH 024/253] questions added in card --- src/components/Cards/QueryCard.jsx | 140 +++++++++++++++++------------ 1 file changed, 83 insertions(+), 57 deletions(-) diff --git a/src/components/Cards/QueryCard.jsx b/src/components/Cards/QueryCard.jsx index 8c76fea..1068541 100644 --- a/src/components/Cards/QueryCard.jsx +++ b/src/components/Cards/QueryCard.jsx @@ -27,7 +27,13 @@ import OptionsPreview from "../Lists/OptionsPreview"; import { getUserName } from "@/api/base"; import { GlobalContext } from "@/contexts/GlobalContext"; import queryApi from "@/api/queryApi"; -import { Delete, Save, GetApp, Rule } from "@mui/icons-material"; +import { + Delete, + Save, + GetApp, + Rule, + AssignmentTurnedIn, +} from "@mui/icons-material"; import { useAuth } from "@/contexts/AuthContext"; import { showError, showSuccess } from "@/contexts/ToastProvider"; import { FormControl, Select, MenuItem, InputLabel } from "@mui/material"; @@ -36,7 +42,7 @@ import { convertFromSnake } from "@/services/utils"; import { AppContext } from "@/contexts/AppContext"; import ContextGeneratorService from "@/services/contextGeneratorService"; import ContextVisualize from "../Viewer/ContextVisualize"; - +import Pluralize from "pluralize"; export default function QueryCard({ entry, onEdit, isPersonal, mode, index }) { const [flag, setFlag] = useState(false); const { setQueries } = useContext(AppContext); @@ -309,8 +315,8 @@ export default function QueryCard({ entry, onEdit, isPersonal, mode, index }) { )} {(entry.username === getUserName() || getUserName() === "admin") && ( -
- {getUserName() === "admin" && ( +
+ {getUserName() === "admin" ? (
{/*
+ ) : ( + } + label={Pluralize( + "Question", + entry.questions.length ?? 0, + true + )} + // size="small" + color="primary" + variant="outlined" + /> )} - {mode === "explore" ? ( - <> - - - ) : ( - <> - {/* */} - - - - - )} + + + + + )} +
)} From 81ec504f30a6ca9e657731af2bd3db9d5a1376e6 Mon Sep 17 00:00:00 2001 From: Mahir Labib Dihan Date: Thu, 15 Aug 2024 12:50:53 +0600 Subject: [PATCH 025/253] categories fixed --- src/app/old-home/QueryFields.jsx | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/src/app/old-home/QueryFields.jsx b/src/app/old-home/QueryFields.jsx index cb4efe9..aa0ebdc 100644 --- a/src/app/old-home/QueryFields.jsx +++ b/src/app/old-home/QueryFields.jsx @@ -568,19 +568,13 @@ export default function QueryFields({ }} input={} > - {[ - "nearby_poi", - "planning", - "time_calculation", - "routing", - "location_finding", - "opinion", - "navigation", - ].map((value, index) => ( - - {value} - - ))} + {["poi", "nearby", "routing", "trip"].map( + (value, index) => ( + + {value} + + ) + )}
From 97c473cae0925e128a3c60702ee90d7617c6e060 Mon Sep 17 00:00:00 2001 From: Mahir Labib Dihan Date: Thu, 15 Aug 2024 13:33:47 +0600 Subject: [PATCH 026/253] Category Chart added --- package-lock.json | 131 +++++++++++++++++++++++++- package.json | 2 + src/app/old-home/ContextGenerator.jsx | 4 +- src/app/old-home/DatasetCreator.jsx | 2 + src/app/old-home/PieChart.jsx | 93 ++++++++++++++++++ src/app/old-home/QueryCard.jsx | 31 +++--- src/app/old-home/categories.json | 2 +- src/database/categories.json | 2 +- 8 files changed, 244 insertions(+), 23 deletions(-) create mode 100644 src/app/old-home/PieChart.jsx diff --git a/package-lock.json b/package-lock.json index 1da9cee..8165dd6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "googlemap", + "name": "MapQaTor", "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "googlemap", + "name": "MapQaTor", "version": "0.1.0", "dependencies": { "@emotion/cache": "^11.11.0", @@ -21,6 +21,7 @@ "@mui/x-date-pickers": "^7.7.0", "@react-google-maps/api": "^2.19.3", "@vis.gl/react-google-maps": "^0.9.0", + "apexcharts": "^3.52.0", "axios": "^1.6.8", "date-fns": "^3.6.0", "dayjs": "^1.11.11", @@ -31,6 +32,7 @@ "next": "14.2.2", "pluralize": "^8.0.0", "react": "^18", + "react-apexcharts": "^1.4.1", "react-dom": "^18", "react-toastify": "^10.0.5", "use-places-autocomplete": "^4.0.1" @@ -1450,6 +1452,12 @@ "react-dom": ">=16.8.0" } }, + "node_modules/@yr/monotone-cubic-spline": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@yr/monotone-cubic-spline/-/monotone-cubic-spline-1.0.3.tgz", + "integrity": "sha512-FQXkOta0XBSUPHndIKON2Y9JeQz5ZeMqLYZVVK93FliNBFm7LNMIZmY6FrMEB9XPcDbE2bekMbZD6kzDkxwYjA==", + "license": "MIT" + }, "node_modules/acorn": { "version": "8.11.3", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", @@ -1530,6 +1538,21 @@ "node": ">= 8" } }, + "node_modules/apexcharts": { + "version": "3.52.0", + "resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.52.0.tgz", + "integrity": "sha512-7dg0ADKs8AA89iYMZMe2sFDG0XK5PfqllKV9N+i3hKHm3vEtdhwz8AlXGm+/b0nJ6jKiaXsqci5LfVxNhtB+dA==", + "license": "MIT", + "dependencies": { + "@yr/monotone-cubic-spline": "^1.0.3", + "svg.draggable.js": "^2.2.2", + "svg.easing.js": "^2.0.0", + "svg.filter.js": "^2.0.2", + "svg.pathmorphing.js": "^0.1.3", + "svg.resize.js": "^1.4.3", + "svg.select.js": "^3.0.1" + } + }, "node_modules/arg": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", @@ -4824,6 +4847,19 @@ "node": ">=0.10.0" } }, + "node_modules/react-apexcharts": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/react-apexcharts/-/react-apexcharts-1.4.1.tgz", + "integrity": "sha512-G14nVaD64Bnbgy8tYxkjuXEUp/7h30Q0U33xc3AwtGFijJB9nHqOt1a6eG0WBn055RgRg+NwqbKGtqPxy15d0Q==", + "license": "MIT", + "dependencies": { + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "apexcharts": "^3.41.0", + "react": ">=0.13" + } + }, "node_modules/react-dom": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", @@ -5487,6 +5523,97 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/svg.draggable.js": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/svg.draggable.js/-/svg.draggable.js-2.2.2.tgz", + "integrity": "sha512-JzNHBc2fLQMzYCZ90KZHN2ohXL0BQJGQimK1kGk6AvSeibuKcIdDX9Kr0dT9+UJ5O8nYA0RB839Lhvk4CY4MZw==", + "license": "MIT", + "dependencies": { + "svg.js": "^2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/svg.easing.js": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/svg.easing.js/-/svg.easing.js-2.0.0.tgz", + "integrity": "sha512-//ctPdJMGy22YoYGV+3HEfHbm6/69LJUTAqI2/5qBvaNHZ9uUFVC82B0Pl299HzgH13rKrBgi4+XyXXyVWWthA==", + "license": "MIT", + "dependencies": { + "svg.js": ">=2.3.x" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/svg.filter.js": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/svg.filter.js/-/svg.filter.js-2.0.2.tgz", + "integrity": "sha512-xkGBwU+dKBzqg5PtilaTb0EYPqPfJ9Q6saVldX+5vCRy31P6TlRCP3U9NxH3HEufkKkpNgdTLBJnmhDHeTqAkw==", + "license": "MIT", + "dependencies": { + "svg.js": "^2.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/svg.js": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/svg.js/-/svg.js-2.7.1.tgz", + "integrity": "sha512-ycbxpizEQktk3FYvn/8BH+6/EuWXg7ZpQREJvgacqn46gIddG24tNNe4Son6omdXCnSOaApnpZw6MPCBA1dODA==", + "license": "MIT" + }, + "node_modules/svg.pathmorphing.js": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/svg.pathmorphing.js/-/svg.pathmorphing.js-0.1.3.tgz", + "integrity": "sha512-49HWI9X4XQR/JG1qXkSDV8xViuTLIWm/B/7YuQELV5KMOPtXjiwH4XPJvr/ghEDibmLQ9Oc22dpWpG0vUDDNww==", + "license": "MIT", + "dependencies": { + "svg.js": "^2.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/svg.resize.js": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/svg.resize.js/-/svg.resize.js-1.4.3.tgz", + "integrity": "sha512-9k5sXJuPKp+mVzXNvxz7U0uC9oVMQrrf7cFsETznzUDDm0x8+77dtZkWdMfRlmbkEEYvUn9btKuZ3n41oNA+uw==", + "license": "MIT", + "dependencies": { + "svg.js": "^2.6.5", + "svg.select.js": "^2.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/svg.resize.js/node_modules/svg.select.js": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-2.1.2.tgz", + "integrity": "sha512-tH6ABEyJsAOVAhwcCjF8mw4crjXSI1aa7j2VQR8ZuJ37H2MBUbyeqYr5nEO7sSN3cy9AR9DUwNg0t/962HlDbQ==", + "license": "MIT", + "dependencies": { + "svg.js": "^2.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/svg.select.js": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-3.0.1.tgz", + "integrity": "sha512-h5IS/hKkuVCbKSieR9uQCj9w+zLHoPh+ce19bBYyqF53g6mnPB8sAtIbe1s9dh2S2fCmYX2xel1Ln3PJBbK4kw==", + "license": "MIT", + "dependencies": { + "svg.js": "^2.6.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/tailwindcss": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.3.tgz", diff --git a/package.json b/package.json index ff1cbb3..e90c848 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "@mui/x-date-pickers": "^7.7.0", "@react-google-maps/api": "^2.19.3", "@vis.gl/react-google-maps": "^0.9.0", + "apexcharts": "^3.52.0", "axios": "^1.6.8", "date-fns": "^3.6.0", "dayjs": "^1.11.11", @@ -33,6 +34,7 @@ "next": "14.2.2", "pluralize": "^8.0.0", "react": "^18", + "react-apexcharts": "^1.4.1", "react-dom": "^18", "react-toastify": "^10.0.5", "use-places-autocomplete": "^4.0.1" diff --git a/src/app/old-home/ContextGenerator.jsx b/src/app/old-home/ContextGenerator.jsx index 2bfdef4..a74b96b 100644 --- a/src/app/old-home/ContextGenerator.jsx +++ b/src/app/old-home/ContextGenerator.jsx @@ -504,7 +504,7 @@ export default function ContextGenerator({ setSelectedPlacesMap, }} /> - + {/* + /> */} {/* +
diff --git a/src/app/old-home/PieChart.jsx b/src/app/old-home/PieChart.jsx new file mode 100644 index 0000000..4cd3ea9 --- /dev/null +++ b/src/app/old-home/PieChart.jsx @@ -0,0 +1,93 @@ +import ApexCharts from "react-apexcharts"; +import { Divider } from "@mui/material"; +import { useEffect, useState } from "react"; + +export default function PieChart({ queries }) { + const [chart, setChart] = useState(undefined); + const [data, setData] = useState({ + poi: 0, + nearby: 0, + routing: 0, + trip: 0, + }); + useEffect(() => { + queries.forEach((query) => { + data[query.classification] += 1; + }); + setChart({ + series: [data.poi, data.nearby, data.routing, data.trip], + options: { + chart: { + type: "pie", + height: "100%", + }, + states: { + active: { + filter: { + type: "none" /* none, lighten, darken */, + }, + }, + hover: { + filter: { + type: "lighten", + value: 0.0001, + }, + }, + }, + plotOptions: { + pie: { + dataLabels: { + offset: -20, + }, + }, + }, + labels: ["Poi", "Nearby", "Routing", "Trip"], + // colors: ["#ef9c9c", "#96cdbf"], + dataLabels: { + position: "top", + offset: -50, // Adjust this value as needed + style: { + // colors: ["#222222", "#222222"], // Add this line. This will make the labels dark black. + offset: -50, // Adjust this value as needed + fontSize: "20px", + shadow: false, + }, + }, + stroke: { + width: 0, // Add this line. This will remove the border. + }, + legend: { + position: "bottom", // or 'top', 'left', 'right' + }, + }, + }); + }, [queries]); + //return the pie chart + return ( +
+

+ Category Distribution +

+ +
+ {chart !== undefined && queries.length > 0 && ( + + )} +
+ + {chart === undefined || + (queries.length == 0 && ( +
+
+ No data available +
+
+ ))} +
+ ); +} diff --git a/src/app/old-home/QueryCard.jsx b/src/app/old-home/QueryCard.jsx index b90ba0d..a9f3058 100644 --- a/src/app/old-home/QueryCard.jsx +++ b/src/app/old-home/QueryCard.jsx @@ -26,7 +26,8 @@ import QueryFields from "./QueryFields"; import dayjs from "dayjs"; import { Clear, Save } from "@mui/icons-material"; import mapApi from "@/api/mapApi"; - +import categories from "@/database/categories"; +import { showSuccess } from "./home"; export default function QueryCard({ initQuery, index, @@ -148,30 +149,19 @@ export default function QueryCard({ Category { - setNewDirection((prev) => ({ - ...prev, - intermediates: event.target.value, - })); - }} - label={"Intermediates"} - renderValue={(selected) => - !newDirection.optimizeWaypointOrder ? ( - - {selected.map((value, index) => ( - <> - - { + {newDirection.travelMode !== "TRANSIT" && ( + <> + + + Intermediates + - -
- If your desired place is not listed here, you need - to{" "} - - add it - {" "} - first. -
-
- - { - setNewDirection((prev) => ({ - ...prev, - optimizeWaypointOrder: - !prev.optimizeWaypointOrder, - })); - }} - checked={newDirection.optimizeWaypointOrder} - size="small" - disabled={newDirection.intermediates.length < 2} - /> -
- Optimize intermediates order -
-
- - )} + size="small" + /> + ))} +
+ ) + } + > + {Object.keys(savedPlacesMap).map( + (place_id) => ( + + { + savedPlacesMap[place_id] + .displayName.text + } + + ) + )} + + +
+ If your desired place is not listed here, you + need to{" "} + + add it + {" "} + first. +
+ + + { + setNewDirection((prev) => ({ + ...prev, + optimizeWaypointOrder: + !prev.optimizeWaypointOrder, + })); + }} + checked={newDirection.optimizeWaypointOrder} + size="small" + disabled={newDirection.intermediates.length < 2} + /> +
+ Optimize intermediates order +
+
+ + )} - {/* {(newDirection.travelMode === "transit" || + {/* {(newDirection.travelMode === "transit" || newDirection.travelMode === "driving") && ( )} */} - {/* {newDirection.from && newDirection.to && ( + {/* {newDirection.from && newDirection.to && ( + )} */} + + + + + Map View + + + + + + {mode === "edit" && ( diff --git a/src/components/Cards/SavedPlaceCard.jsx b/src/components/Cards/SavedPlaceCard.jsx index 1f1464b..c3de161 100644 --- a/src/components/Cards/SavedPlaceCard.jsx +++ b/src/components/Cards/SavedPlaceCard.jsx @@ -55,15 +55,19 @@ export default function SavedPlaceCard({ placeId, savedPlacesMap }) { if (place_id === "" || selectedPlacesMap[place_id]) return; setSelectedPlacesMap((prev) => ({ ...prev, + // [place_id]: { + // selectedAttributes: Object.keys(details).filter( + // (key) => + // details[key] !== null && textualFields.includes(key) + // ), + // attributes: Object.keys(details).filter( + // (key) => + // details[key] !== null && textualFields.includes(key) + // ), + // }, [place_id]: { - selectedAttributes: Object.keys(details).filter( - (key) => - details[key] !== null && textualFields.includes(key) - ), - attributes: Object.keys(details).filter( - (key) => - details[key] !== null && textualFields.includes(key) - ), + selectedAttributes: textualFields, + attributes: textualFields, }, })); }; @@ -73,7 +77,7 @@ export default function SavedPlaceCard({ placeId, savedPlacesMap }) {
- {savedPlacesMap[placeId].displayName.text} + {savedPlacesMap[placeId].displayName?.text} {savedPlacesMap[placeId].shortFormattedAddress} diff --git a/src/components/Forms/DirectionForm.jsx b/src/components/Forms/DirectionForm.jsx index 03a3546..652c40f 100644 --- a/src/components/Forms/DirectionForm.jsx +++ b/src/components/Forms/DirectionForm.jsx @@ -285,7 +285,7 @@ export default function DirectionForm({ label={ savedPlacesMap[ value - ].displayName.text + ].displayName?.text } size="small" /> @@ -302,7 +302,7 @@ export default function DirectionForm({ > { savedPlacesMap[place_id] - .displayName.text + .displayName?.text } ) diff --git a/src/components/Forms/NearbyForm.jsx b/src/components/Forms/NearbyForm.jsx index 6c15472..d433f44 100644 --- a/src/components/Forms/NearbyForm.jsx +++ b/src/components/Forms/NearbyForm.jsx @@ -126,7 +126,7 @@ export default function NearbyForm({ const placesWithSelection = places.map((place) => ({ // selected: true, place_id: place.id, - name: place.displayName.text, + name: place.displayName?.text, formatted_address: place.shortFormattedAddress, rating: place.rating, priceLevel: place.priceLevel @@ -254,7 +254,7 @@ export default function NearbyForm({ : newNearbyPlaces.keyword }s near ${ savedPlacesMap[newNearbyPlaces.locationBias] - .displayName.text + .displayName?.text }`} > diff --git a/src/components/Forms/PlacesForm.jsx b/src/components/Forms/PlacesForm.jsx index 411412d..8965677 100644 --- a/src/components/Forms/PlacesForm.jsx +++ b/src/components/Forms/PlacesForm.jsx @@ -40,14 +40,8 @@ export default function PlacesForm({ handlePlaceAdd }) { setSelectedPlacesMap((prev) => ({ ...prev, [place_id]: { - selectedAttributes: Object.keys(details).filter( - (key) => - details[key] !== null && textualFields.includes(key) - ), - attributes: Object.keys(details).filter( - (key) => - details[key] !== null && textualFields.includes(key) - ), + selectedAttributes: textualFields, + attributes: textualFields, }, })); }; diff --git a/src/components/GoogleMap/MapComponent.jsx b/src/components/GoogleMap/MapComponent.jsx index e928d30..b98f3c6 100644 --- a/src/components/GoogleMap/MapComponent.jsx +++ b/src/components/GoogleMap/MapComponent.jsx @@ -5,7 +5,7 @@ import { GlobalContext } from "@/contexts/GlobalContext"; import { GoogleMap, LoadScript, Marker } from "@react-google-maps/api"; import { useContext, useEffect, useState } from "react"; -export default function MapComponent({ locations }) { +export default function MapComponent({ locations, height, zoom }) { const { selectedPlacesMap } = useContext(GlobalContext); const { savedPlacesMap } = useContext(AppContext); // const [locations, setLocations] = useState([]); @@ -29,7 +29,7 @@ export default function MapComponent({ locations }) { // }, [savedPlacesMap]); const mapStyles = { - height: "400px", + height: height || "400px", width: "100%", }; @@ -37,7 +37,7 @@ export default function MapComponent({ locations }) { locations.length > 0 && ( {locations.map((location, index) => ( diff --git a/src/components/Grids/NearbyGrid.jsx b/src/components/Grids/NearbyGrid.jsx index 3e9b0ed..fdc6983 100644 --- a/src/components/Grids/NearbyGrid.jsx +++ b/src/components/Grids/NearbyGrid.jsx @@ -16,7 +16,7 @@ export default function NearbyGrid({ {Object.keys(nearbyPlacesMap).map((place_id, i) => nearbyPlacesMap[place_id].map((entry, index) => ( - + ( - + @@ -221,7 +221,7 @@ export default function AutocompleteSearchBox() {