diff --git a/.github/workflows/nextjs.yml b/.github/workflows/nextjs.yml
index da10ec2..8fe70a6 100644
--- a/.github/workflows/nextjs.yml
+++ b/.github/workflows/nextjs.yml
@@ -7,7 +7,7 @@ name: Deploy Next.js site to Pages
on:
# Runs on pushes targeting the default branch
push:
- branches: ["master"]
+ branches: ["main"]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
diff --git a/README.md b/README.md
index 0dc9ea2..4b32b96 100644
--- a/README.md
+++ b/README.md
@@ -1,36 +1,199 @@
+# MapQaTor Frontend
+
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
-## Getting Started
+## Project Setup
+
+Follow the step by step installation procedure to install and run this on your machine.
+
+## Prerequisites
+
+Make sure you have node installed in your device.
+
+**`NodeJs`**: Install Nodejs from [here](https://nodejs.org/en/download/)
+
+## Installation
+
+
+1. Clone the repo
+
+```sh
+git clone https://github.com/mapqator/mapqator.github.io.git
+```
+
+2. If you don't have git installed in your device then download zip
+3. After installation or download go to the repository and open command line.
+
+### Configuring
+
+
+2. Install NPM packages
-First, run the development server:
+```sh
+npm install
+```
+
+3. Ensure backend is configured and running correctly on "http://localhost:5000" (note the port number)
+
+#### Run the project
+
+Go to your favourite code editor and run
-```bash
+```sh
npm run dev
-# or
-yarn dev
-# or
-pnpm dev
-# or
-bun dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
-You can start editing the page by modifying `app/page.js`. The page auto-updates as you edit the file.
+You should find that the project is working!
+
+## Adding new Map APIs
+
+Add or edit files in the `src/tools` and `src/mapServices` directories.
+
+
+# Data Collection Tools
+
+
+
+## Text Search Object
+
+| Field Name | Description |
+|------------------|--------------------------------------------------|
+| `id` | A location identifier (e.g., a Place ID) |
+| `displayName` | Display name of the location. |
+| `shortFormattedAddress` | Short address of the location. |
+| `location` | Latitude and longitude of the location. |
+| `mapService` | Map service used for the search (e.g., googleMaps). |
+
+## Place Details Object
+
+Attributes of the place details object are unique to the Map Service used. This need to be specified in the adapter. Attributes should be subset of the following:
+
+`[ "location",
+ "shortFormattedAddress",
+ "accessibilityOptions",
+ "businessStatus",
+ "googleMapsUri",
+ "primaryType",
+ "internationalPhoneNumber",
+ "nationalPhoneNumber",
+ "priceLevel",
+ "rating",
+ "regularOpeningHours",
+ "userRatingCount",
+ "websiteUri",
+ "allowsDogs",
+ "curbsidePickup",
+ "delivery",
+ "dineIn",
+ "editorialSummary",
+ "evChargeOptions",
+ "fuelOptions",
+ "goodForChildren",
+ "goodForGroups",
+ "goodForWatchingSports",
+ "liveMusic",
+ "menuForChildren",
+ "parkingOptions",
+ "paymentOptions",
+ "outdoorSeating",
+ "reservable",
+ "restroom",
+ "servesBeer",
+ "servesBreakfast",
+ "servesBrunch",
+ "servesCocktails",
+ "servesCoffee",
+ "servesDessert",
+ "servesDinner",
+ "servesLunch",
+ "servesVegetarianFood",
+ "servesWine",
+ "takeout",
+ "generativeSummary",
+ "areaSummary"
+]
+`
-This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
+## Nearby Search Object
-## Learn More
+| Field Name | Description |
+|------------------|--------------------------------------------------|
+| `locationBias` | A location identifier (e.g., a Place ID) specifying the place around which to search |
+| `type` | Type of places being searched (e.g., restaurant). |
+| `keyword` | A keyword for refining the search (can be empty). |
+| `minRating` | Minimum rating filter for the search (e.g., 4). |
+| `priceLevels`| List of price levels allowed (e.g., PRICE_LEVEL_MODERATE, PRICE_LEVEL_EXPENSIVE). |
+| `rankPreference` | Ranking preference for search results (RELEVANCE, DISTANCE, etc.). |
+| `maxResultCount` | Maximum number of results to return. |
+| `places` | List of places matching the query criteria. |
+| `routingSummaries` | List of navigation details for reaching the places. |
-To learn more about Next.js, take a look at the following resources:
+## Compute Routes Object
-- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
-- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
+
-You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
+| Field Name | Description |
+|------------------|--------------------------------------------------|
+| `origin` | Origin location identifier. |
+| `destination` | Destination location identifier. |
+| `intermediates` | List of intermediate locations. |
+| `travelMode` | Mode of travel (e.g., DRIVE, WALK). |
+| `optimizeWaypointOrder` | Optimize the order of waypoints. |
+| `routeModifiers` | Modifiers for the route (e.g., avoidTolls). |
+| `routes` | List of routes from origin to destination. |
-## Deploy on Vercel
+## Search Along Route Object
-The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
+
-Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
+| Field Name | Description |
+|------------------|--------------------------------------------------|
+| `type` | Type of places being searched (e.g., restaurant). |
+| `minRating` | Minimum rating filter for the search (e.g., 4). |
+| `priceLevels` | List of price levels allowed (e.g., PRICE_LEVEL_MODERATE, PRICE_LEVEL_EXPENSIVE). |
+| `rankPreference` | Ranking preference for search results (RELEVANCE, DISTANCE, etc.). |
+| `origin` | Origin location identifier. |
+| `destination` | Destination location identifier. |
+| `travelMode` | Mode of travel (e.g., DRIVE, WALK). |
+| `routeModifiers` | Modifiers for the route (e.g., avoidTolls). |
+| `maxResultCount` | Maximum number of results to return. |
+| `places` | List of places matching the query criteria. |
+| `routes` | List of routes from origin to destination. |
diff --git a/package-lock.json b/package-lock.json
index 1da9cee..d58e6c9 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,7 +32,9 @@
"next": "14.2.2",
"pluralize": "^8.0.0",
"react": "^18",
+ "react-apexcharts": "^1.4.1",
"react-dom": "^18",
+ "react-icons": "^5.3.0",
"react-toastify": "^10.0.5",
"use-places-autocomplete": "^4.0.1"
},
@@ -1450,6 +1453,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 +1539,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 +4848,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",
@@ -4836,6 +4873,15 @@
"react": "^18.2.0"
}
},
+ "node_modules/react-icons": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.3.0.tgz",
+ "integrity": "sha512-DnUk8aFbTyQPSkCfF8dbX6kQjXA9DktMeJqfjrg6cK9vwQVMxmcA3BfP4QoiztVmEHtwlTgLFsPuH2NskKT6eg==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "*"
+ }
+ },
"node_modules/react-is": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
@@ -5487,6 +5533,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..4e82b7e 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,7 +34,9 @@
"next": "14.2.2",
"pluralize": "^8.0.0",
"react": "^18",
+ "react-apexcharts": "^1.4.1",
"react-dom": "^18",
+ "react-icons": "^5.3.0",
"react-toastify": "^10.0.5",
"use-places-autocomplete": "^4.0.1"
},
diff --git a/public/images/azure-maps.png b/public/images/azure-maps.png
new file mode 100644
index 0000000..3a963c5
Binary files /dev/null and b/public/images/azure-maps.png differ
diff --git a/public/images/google-maps.png b/public/images/google-maps.png
new file mode 100644
index 0000000..6c2c77a
Binary files /dev/null and b/public/images/google-maps.png differ
diff --git a/public/images/graphhopper.png b/public/images/graphhopper.png
new file mode 100644
index 0000000..712a368
Binary files /dev/null and b/public/images/graphhopper.png differ
diff --git a/public/images/here.png b/public/images/here.png
new file mode 100644
index 0000000..3551fef
Binary files /dev/null and b/public/images/here.png differ
diff --git a/public/images/mapbox-blue.png b/public/images/mapbox-blue.png
new file mode 100644
index 0000000..24577ef
Binary files /dev/null and b/public/images/mapbox-blue.png differ
diff --git a/public/images/mapbox.png b/public/images/mapbox.png
new file mode 100644
index 0000000..fa5f194
Binary files /dev/null and b/public/images/mapbox.png differ
diff --git a/public/images/openstreetmap.logo.png b/public/images/openstreetmap.logo.png
new file mode 100644
index 0000000..ab51808
Binary files /dev/null and b/public/images/openstreetmap.logo.png differ
diff --git a/public/images/openstreetmap.logo.svg b/public/images/openstreetmap.logo.svg
new file mode 100644
index 0000000..c5e9a6a
--- /dev/null
+++ b/public/images/openstreetmap.logo.svg
@@ -0,0 +1,316 @@
+
+
+
diff --git a/public/images/tomtom.cover.png b/public/images/tomtom.cover.png
new file mode 100644
index 0000000..860e99b
Binary files /dev/null and b/public/images/tomtom.cover.png differ
diff --git a/public/images/tomtom.jpg b/public/images/tomtom.jpg
new file mode 100644
index 0000000..bffdfcf
Binary files /dev/null and b/public/images/tomtom.jpg differ
diff --git a/public/images/tomtom.png b/public/images/tomtom.png
new file mode 100644
index 0000000..54d8b52
Binary files /dev/null and b/public/images/tomtom.png differ
diff --git a/src/api/evaluationApi.js b/src/api/evaluationApi.js
new file mode 100644
index 0000000..9715f1e
--- /dev/null
+++ b/src/api/evaluationApi.js
@@ -0,0 +1,10 @@
+import Api from "./base";
+
+class EvaluationApi extends Api {
+ insertNewResult = async (list) => {
+ return await this.post("/evaluation/new", list);
+ };
+}
+
+const evaluationApi = new EvaluationApi();
+export default evaluationApi;
diff --git a/src/api/geminiApi.js b/src/api/geminiApi.js
index 84b5add..acc1e4f 100644
--- a/src/api/geminiApi.js
+++ b/src/api/geminiApi.js
@@ -1,9 +1,8 @@
import Api from "./base";
class GeminiApi extends Api {
- askGeminiLive = async (context, query) => {
- console.log(context, query);
- return await this.post("/gemini/ask", { context, query });
+ askGeminiLive = async (prompt) => {
+ return await this.post("/gemini/ask", { prompt });
};
}
diff --git a/src/api/googleMapsApi.js b/src/api/googleMapsApi.js
new file mode 100644
index 0000000..7dec5a4
--- /dev/null
+++ b/src/api/googleMapsApi.js
@@ -0,0 +1,351 @@
+import MapsApi from "./mapsApi";
+
+class GoogleMapsApi extends MapsApi {
+ textSearch = async (query) => {
+ const apiCall = {
+ url: "https://places.googleapis.com/v1/places:searchText",
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ "X-Goog-FieldMask":
+ "places.id,places.displayName,places.shortFormattedAddress,places.location",
+ // "X-Goog-Api-Key": ************************,
+ },
+ body: {
+ textQuery: query,
+ maxResultCount: 5,
+ },
+ };
+ const epochId = Date.now(); // Unique ID for this tool call
+ const response = await this.post("/map/cached", apiCall);
+
+ if (response.success) {
+ return {
+ success: true,
+ data: {
+ result: response.data,
+ apiCallLogs: [
+ {
+ ...apiCall,
+ uuid: epochId,
+ result: response.data,
+ },
+ ],
+ uuid: epochId,
+ },
+ };
+ }
+ return {
+ success: false,
+ };
+ };
+
+ placeDetails = async (place_id) => {
+ const apiCall = {
+ url: "https://places.googleapis.com/v1/places/" + place_id,
+ method: "GET",
+ headers: {
+ "Content-Type": "application/json",
+ "X-Goog-FieldMask":
+ "id,addressComponents,adrFormatAddress,formattedAddress,location,shortFormattedAddress,types,viewport,accessibilityOptions,businessStatus,displayName,googleMapsUri,primaryType,primaryTypeDisplayName,internationalPhoneNumber,nationalPhoneNumber,priceLevel,rating,regularOpeningHours.weekdayDescriptions,userRatingCount,websiteUri,allowsDogs,curbsidePickup,delivery,dineIn,goodForChildren,goodForGroups,goodForWatchingSports,liveMusic,menuForChildren,outdoorSeating,reservable,restroom,servesBeer,servesBreakfast,servesBrunch,servesCocktails,servesCoffee,servesDessert,servesDinner,servesLunch,servesVegetarianFood,servesWine,takeout",
+ // "X-Goog-Api-Key": ************************,
+ },
+ };
+ const epochId = Date.now(); // Unique ID for this tool call
+ const response = await this.post("/map/cached", apiCall);
+
+ if (response.success) {
+ return {
+ success: true,
+ data: {
+ result: response.data,
+ apiCallLogs: [
+ {
+ ...apiCall,
+ uuid: epochId,
+ result: response.data,
+ },
+ ],
+ uuid: epochId,
+ },
+ };
+ }
+ return {
+ success: false,
+ };
+ };
+
+ nearbySearch = async (params) => {
+ const apiCall = {
+ url: "https://places.googleapis.com/v1/places:searchText",
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ "X-Goog-FieldMask":
+ "places.id,places.displayName,places.formattedAddress,places.rating,places.priceLevel,places.shortFormattedAddress,places.userRatingCount,places.location,routingSummaries",
+ // "X-Goog-Api-Key": ************************,
+ },
+ body: {
+ textQuery: params.type + " " + params.keyword,
+ rankPreference: params.rankPreference || "RELEVANCE", // DISTANCE/RELEVANCE/RANK_PREFERENCE_UNSPECIFIED
+ includedType: params.type, // One type only
+ minRating: params.minRating,
+ priceLevels: params.priceLevels,
+ maxResultCount: params.maxResultCount || 5,
+ strictTypeFiltering: true,
+ locationBias: {
+ circle: {
+ center: {
+ latitude: params.lat,
+ longitude: params.lng,
+ },
+ radius: 0,
+ },
+ },
+ languageCode: "en",
+ routingParameters: {
+ origin: {
+ latitude: params.lat,
+ longitude: params.lng,
+ },
+ travelMode: "WALK",
+ },
+ },
+ };
+ const epochId = Date.now(); // Unique ID for this tool call
+ const response = await this.post("/map/cached", apiCall);
+
+ if (response.success) {
+ return {
+ success: true,
+ data: {
+ result: response.data,
+ apiCallLogs: [
+ {
+ ...apiCall,
+ uuid: epochId,
+ result: response.data,
+ },
+ ],
+ uuid: epochId,
+ },
+ };
+ }
+ return {
+ success: false,
+ };
+ };
+
+ computeRoutes = async (params) => {
+ const apiCall = {
+ url: "https://routes.googleapis.com/directions/v2:computeRoutes",
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ "X-Goog-FieldMask":
+ "routes.distanceMeters,routes.staticDuration,routes.description,routes.localizedValues,routes.optimized_intermediate_waypoint_index,routes.legs.steps.navigationInstruction,routes.legs.steps.transitDetails,routes.legs.localizedValues,routes.legs.steps.travelMode,routes.legs.steps.localizedValues,routes.legs.polyline,routes.polyline",
+ // "X-Goog-Api-Key": ************************,
+ },
+ body: {
+ origin: {
+ placeId: params.origin,
+ },
+ destination: {
+ placeId: params.destination,
+ },
+ travelMode: params.travelMode,
+ intermediates:
+ params.travelMode !== "TRANSIT"
+ ? params.intermediates?.map((intermediate) => ({
+ placeId: intermediate,
+ }))
+ : undefined,
+ routeModifiers: {
+ avoidTolls: ["DRIVE", "TWO_WHEELER"].includes(
+ params.travelMode
+ )
+ ? params.routeModifiers.avoidTolls
+ : false,
+ avoidHighways: ["DRIVE", "TWO_WHEELER"].includes(
+ params.travelMode
+ )
+ ? params.routeModifiers.avoidHighways
+ : false,
+ avoidFerries: ["DRIVE", "TWO_WHEELER"].includes(
+ params.travelMode
+ )
+ ? params.routeModifiers.avoidFerries
+ : false,
+ avoidIndoor: false,
+ },
+ transitPreferences:
+ params.travelMode === "TRANSIT"
+ ? params.transitPreferences
+ : undefined,
+ optimizeWaypointOrder:
+ params.intermediates.length > 0 &&
+ params.travelMode !== "TRANSIT"
+ ? params.optimizeWaypointOrder
+ : false,
+ extraComputations:
+ params.travelMode === "TRANSIT"
+ ? []
+ : ["HTML_FORMATTED_NAVIGATION_INSTRUCTIONS"],
+ units: "METRIC",
+ languageCode: "en",
+ routingPreference:
+ params.travelMode === "WALK" ||
+ params.travelMode === "BICYCLE" ||
+ params.travelMode === "TRANSIT"
+ ? undefined
+ : "TRAFFIC_UNAWARE",
+ computeAlternativeRoutes:
+ params.intermediates.length === 0
+ ? params.computeAlternativeRoutes === undefined
+ ? true
+ : params.computeAlternativeRoutes
+ : false,
+ },
+ };
+
+ const epochId = Date.now(); // Unique ID for this tool call
+ const response = await this.post("/map/cached", apiCall);
+
+ if (response.success) {
+ return {
+ success: true,
+ data: {
+ result: response.data,
+ apiCallLogs: [
+ {
+ ...apiCall,
+ uuid: epochId,
+ result: response.data,
+ },
+ ],
+ uuid: epochId,
+ },
+ };
+ }
+
+ return {
+ success: false,
+ };
+ };
+
+ searchAlongRoute = async (params) => {
+ const apiCall1 = {
+ url: "https://routes.googleapis.com/directions/v2:computeRoutes",
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ "X-Goog-FieldMask":
+ "routes.distanceMeters,routes.staticDuration,routes.description,routes.localizedValues,routes.optimized_intermediate_waypoint_index,routes.legs.steps.navigationInstruction,routes.legs.steps.transitDetails,routes.legs.localizedValues,routes.legs.steps.travelMode,routes.legs.steps.localizedValues,routes.legs.polyline,routes.polyline",
+ // "X-Goog-Api-Key": ************************,
+ },
+ body: {
+ origin: {
+ placeId: params.origin,
+ },
+ destination: {
+ placeId: params.destination,
+ },
+ travelMode: params.travelMode,
+ intermediates: undefined,
+ routeModifiers: {
+ avoidTolls: ["DRIVE", "TWO_WHEELER"].includes(
+ params.travelMode
+ )
+ ? params.routeModifiers.avoidTolls
+ : false,
+ avoidHighways: ["DRIVE", "TWO_WHEELER"].includes(
+ params.travelMode
+ )
+ ? params.routeModifiers.avoidHighways
+ : false,
+ avoidFerries: ["DRIVE", "TWO_WHEELER"].includes(
+ params.travelMode
+ )
+ ? params.routeModifiers.avoidFerries
+ : false,
+ avoidIndoor: false,
+ },
+ transitPreferences: undefined,
+ optimizeWaypointOrder: false,
+ extraComputations:
+ params.travelMode === "TRANSIT"
+ ? []
+ : ["HTML_FORMATTED_NAVIGATION_INSTRUCTIONS"],
+ units: "METRIC",
+ languageCode: "en",
+ routingPreference:
+ params.travelMode === "WALK" ||
+ params.travelMode === "BICYCLE" ||
+ params.travelMode === "TRANSIT"
+ ? undefined
+ : "TRAFFIC_UNAWARE",
+ computeAlternativeRoutes: false,
+ },
+ };
+
+ const response1 = await this.post("/map/cached", apiCall1);
+
+ if (!response1.success) return { success: false };
+ const apiCall2 = {
+ url: "https://places.googleapis.com/v1/places:searchText",
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ "X-Goog-FieldMask":
+ "places.id,places.displayName,places.rating,places.priceLevel,places.shortFormattedAddress,places.userRatingCount,places.location",
+ },
+ body: {
+ textQuery:
+ params.searchBy === "type" ? params.type : params.keyword,
+ rankPreference: params.rankPreference || "RELEVANCE", // DISTANCE/RELEVANCE/RANK_PREFERENCE_UNSPECIFIED
+ includedType:
+ params.searchBy === "type" ? params.type : undefined, // One type only
+ minRating: params.minRating,
+ priceLevels: params.priceLevels,
+ maxResultCount: params.maxResultCount || 5,
+ strictTypeFiltering: true,
+ searchAlongRouteParameters: {
+ polyline: {
+ encodedPolyline:
+ response1.data.routes[0].polyline.encodedPolyline,
+ },
+ },
+ languageCode: "en",
+ },
+ };
+
+ const response2 = await this.post("/map/cached", apiCall2);
+
+ if (!response2.success) return { success: false };
+
+ const epochId = Date.now();
+
+ return {
+ success: true,
+ data: {
+ route_response: response1.data,
+ nearby_response: response2.data,
+ apiCallLogs: [
+ {
+ ...apiCall1,
+ uuid: epochId,
+ result: response1.data,
+ },
+ {
+ ...apiCall2,
+ uuid: epochId,
+ result: response2.data,
+ },
+ ],
+ uuid: epochId,
+ },
+ };
+ };
+}
+const mapsApi = new GoogleMapsApi();
+export default mapsApi;
diff --git a/src/api/gptApi.js b/src/api/gptApi.js
index ea041a4..732062b 100644
--- a/src/api/gptApi.js
+++ b/src/api/gptApi.js
@@ -9,7 +9,7 @@ class GptApi extends Api {
};
askGPTLive = async (context, query) => {
console.log(context, query);
- return await this.post("/gpt/ask", { context, query });
+ return await this.post("/gpt/ask-many", { context, query });
};
generateQuestion = async (context) => {
return await this.post("/gpt/generate-question", { context });
diff --git a/src/api/mapApi.js b/src/api/mapApi.js
index 0cf847b..f5db3c4 100644
--- a/src/api/mapApi.js
+++ b/src/api/mapApi.js
@@ -4,23 +4,32 @@ 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=" +
params.lat +
"&lng=" +
params.lng +
- (params.rankBy !== "distance"
+ (params.rankPreference !== "distance"
? "&radius=" + params.radius
: "") +
(params.type !== "" ? "&type=" + params.type : "") +
(params.keyword !== "" ? "&keyword=" + params.keyword : "") +
"&rankby=" +
- params.rankBy +
+ params.rankPreference +
"&location=" +
params.location
);
};
+ searchAlongRoute = async (params) => {
+ return await this.post("/map/search/along-route", params);
+ };
+ getNearbyNew = async (params) => {
+ return await this.post("/map/nearby/new", params);
+ };
getInside = async (params) => {
if (params.type === "") return;
return await this.get(
@@ -34,6 +43,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 +56,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 +70,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/mapsApi.js b/src/api/mapsApi.js
new file mode 100644
index 0000000..9ec4151
--- /dev/null
+++ b/src/api/mapsApi.js
@@ -0,0 +1,32 @@
+import Api from "./base";
+
+class MapsApi extends Api {
+ constructor() {
+ if (new.target === MapsApi) {
+ throw new TypeError("Cannot construct MapsApi instances directly");
+ }
+ super();
+ }
+ // Abstract methods
+ textSearch() {
+ throw new Error("Method 'textSearch()' must be implemented.");
+ }
+
+ placeDetails() {
+ throw new Error("Method 'placeDetails()' must be implemented.");
+ }
+
+ nearbySearch() {
+ throw new Error("Method 'nearbySearch()' must be implemented.");
+ }
+
+ computeRoutes() {
+ throw new Error("Method 'computeRoutes()' must be implemented.");
+ }
+
+ searchAlongRoute() {
+ throw new Error("Method 'searchAlongRoute()' must be implemented.");
+ }
+}
+
+export default MapsApi;
diff --git a/src/api/queryApi.js b/src/api/queryApi.js
index 18f1af0..95ec1fc 100644
--- a/src/api/queryApi.js
+++ b/src/api/queryApi.js
@@ -1,15 +1,25 @@
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}`);
};
+ getNewQuery = async (id) => {
+ return await this.get(`/queries/new/${id}`);
+ };
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);
};
@@ -19,12 +29,18 @@ class QueryApi extends Api {
updateQuery = async (id, body) => {
return await this.put(`/queries/${id}`, body);
};
+ updateNewQuery = async (id, body) => {
+ return await this.put(`/queries/new/${id}`, body);
+ };
updateQueryWithEvaluation = async (id, body) => {
return await this.put(`/queries/${id}/evaluate`, body);
};
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 });
@@ -32,6 +48,14 @@ 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,
+ });
+ };
+ getModels = async () => {
+ return await this.get("/queries/models");
+ };
}
const queryApi = new QueryApi();
diff --git a/src/app/home/WorkFlow.jsx b/src/app/home/WorkFlow.jsx
index 3b42521..08bc134 100644
--- a/src/app/home/WorkFlow.jsx
+++ b/src/app/home/WorkFlow.jsx
@@ -10,9 +10,12 @@ import QuestionAnswerIcon from "@mui/icons-material/QuestionAnswer";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faRobot } from "@fortawesome/free-solid-svg-icons";
import {
+ Checklist,
Description,
KeyboardDoubleArrowDown,
KeyboardDoubleArrowRight,
+ ListAlt,
+ Save,
} from "@mui/icons-material";
export default function WorkFlow() {
@@ -22,7 +25,7 @@ export default function WorkFlow() {
useEffect(() => {
// Show the tooltip after a short delay
- const timer = setTimeout(() => setShowTooltip(true), 1000);
+ const timer = setTimeout(() => setShowTooltip(true), 500);
return () => clearTimeout(timer);
}, []);
@@ -41,9 +44,8 @@ export default function WorkFlow() {
}}
/>
),
- label: "Generate Context",
- description:
- "Generate rich, place-related contexts using our intuitive interface with Map Services.",
+ label: "Design Context",
+ description: "Design place-related contexts using Map Services.",
key: "context",
to: "/home/context",
},
@@ -55,9 +57,9 @@ export default function WorkFlow() {
}}
/>
),
- label: "Create Question",
+ label: "Create QA Pair",
description:
- "Easily create relevant questions based on the map contexts you've generated.",
+ "Create questions and answers based on designed contexts.",
key: "question",
to: "/home/question",
},
@@ -66,29 +68,44 @@ export default function WorkFlow() {
// label: "Manage Datasets",
// description: "Organize and version your QnA datasets",
// },
+ // {
+ // icon: (
+ //
- No context provided. -
- )} - {text.length > 200 && ( - - )} */} + /> */} + + {Object.keys(savedPlacesMap).length > 0 ? ( ++ No information added. +
+ )}+ No information added. +
+ )} +Generate context for a given question
- -- No context generated. Add information first. -
- )} -- Context generated by our system -
-- Some information about current situation -
-- Write question and answers with appropriate contexts -
- -- Routes from one place to another -
-- Distance and Duration from one place to another -
-- Search for a place from google map or our database -
-- Nearby POIs of a given location -
-- Choose a place from the database -
-- Search for a place and save it to the database -
-- Results of the Points of Interest search -
-- List of all places in the context -
-Context+Question+Answer
-- No context provided. -
- )} - {text.length > 200 && ( - - )} */} -{description}
+No API Available
+ ); +} diff --git a/src/components/GoogleMaps/Forms/DistanceForm.jsx b/src/components/GoogleMaps/Forms/DistanceForm.jsx new file mode 100644 index 0000000..667562a --- /dev/null +++ b/src/components/GoogleMaps/Forms/DistanceForm.jsx @@ -0,0 +1,153 @@ +import React, { useContext, useEffect, useState } from "react"; +import mapApi from "@/api/mapApi"; +import { Grid } from "@mui/material"; +import { LoadingButton } from "@mui/lab"; +import { Add } from "@mui/icons-material"; +import PlaceSelectionField from "@/components/GoogleMaps/InputFields/PlaceSelectionField"; +import TravelSelectionField from "@/mapServices/GoogleMaps/TravelSelectionField"; +import { GlobalContext } from "@/contexts/GlobalContext"; +import { showError } from "@/contexts/ToastProvider"; + +export default function DistanceForm({ + handlePlaceAdd, + newDistance, + setNewDistance, +}) { + const { selectedPlacesMap, distanceMatrix, setDistanceMatrix } = + useContext(GlobalContext); + const initialData = { + origins: [], + destinations: [], + travelMode: "WALK", + }; + + const [loading, setLoading] = useState(false); + + // useEffect(() => { + // setNewDistance(initialData); + // }, []); + + const handleDistanceAdd = async () => { + console.log(newDistance); + if ( + newDistance.origins.length === 0 || + newDistance.destinations.length === 0 + ) + return; + setLoading(true); + // Fetch the distance between the two places from google maps + const response = await mapApi.getDistanceNew(newDistance); + if (response.success) { + const origins = newDistance.origins; + const destinations = newDistance.destinations; + const elements = response.data; + const newDistanceMatrix = { ...distanceMatrix }; + // distanceMatrix[origin][destination][travelMode] = { duration, distance } + for (const route of elements) { + const { + originIndex, + destinationIndex, + condition, + localizedValues, + } = route; + + if ( + origins[originIndex] === destinations[destinationIndex] || + condition === "ROUTE_NOT_FOUND" + ) { + continue; + } + const distance = localizedValues.distance.text; + const duration = localizedValues.staticDuration.text; + const o = origins[originIndex]; + const d = destinations[destinationIndex]; + if (newDistanceMatrix[o]) + newDistanceMatrix[o][d] = { + ...newDistanceMatrix[o][d], + [newDistance.travelMode]: { + duration, + distance, + }, + }; + else { + newDistanceMatrix[o] = { + [d]: { + [newDistance.travelMode]: { + duration, + distance, + }, + }, + }; + } + } + setDistanceMatrix(newDistanceMatrix); + } else { + showError("Couldn't find the distance between the places"); + } + // setNewDistance((prev) => ({ + // ...initialData, + // travelMode: prev.travelMode, + // })); + setLoading(false); + }; + return ( + newDistance && ( +No API Available
+ ); +} diff --git a/src/components/Forms/ParamsForm.jsx b/src/components/GoogleMaps/Forms/ParamsForm.jsx similarity index 87% rename from src/components/Forms/ParamsForm.jsx rename to src/components/GoogleMaps/Forms/ParamsForm.jsx index 53a22a5..ee8cddf 100644 --- a/src/components/Forms/ParamsForm.jsx +++ b/src/components/GoogleMaps/Forms/ParamsForm.jsx @@ -1,10 +1,10 @@ import { IconButton, Box } from "@mui/material"; import { Clear } from "@mui/icons-material"; -import PlaceSelectionField from "@/components/InputFields/PlaceSelectionField"; +import PlaceSelectionField from "@/components/GoogleMaps/InputFields/PlaceSelectionField"; import { useContext } from "react"; import { GlobalContext } from "@/contexts/GlobalContext"; -import DaySelectionField from "../InputFields/DaySelectionField"; -import TimeSelectionField from "../InputFields/TimeSelectionField"; +import DaySelectionField from "../../InputFields/DaySelectionField"; +import TimeSelectionField from "../../InputFields/TimeSelectionField"; import CurrentLocationSelectionField from "../InputFields/CurrentLocationSelectionField"; function ClearButton({ onClick }) { diff --git a/src/components/GoogleMaps/Forms/PlacesForm.jsx b/src/components/GoogleMaps/Forms/PlacesForm.jsx new file mode 100644 index 0000000..62a68ef --- /dev/null +++ b/src/components/GoogleMaps/Forms/PlacesForm.jsx @@ -0,0 +1,202 @@ +/* eslint-disable @next/next/no-img-element */ +import { + Box, + Chip, + FormControl, + Grid, + InputLabel, + MenuItem, + Select, + Typography, +} from "@mui/material"; +import PlaceSelectionField from "../InputFields/PlaceSelectionField"; +import { LoadingButton } from "@mui/lab"; +import { Add } from "@mui/icons-material"; +import { useContext, useEffect, useState } from "react"; +import { AppContext } from "@/contexts/AppContext"; +import { GlobalContext } from "@/contexts/GlobalContext"; +import mapApi from "@/api/mapApi"; +import textualFields from "@/database/textualFields.json"; +import { showError } from "@/contexts/ToastProvider"; +import { list as placeDetailsTools } from "@/tools/PlaceDetails"; +import mapServices from "@/tools/MapServices"; +import { list as placeDetailsList } from "@/tools/PlaceDetails"; + +function UnsavedPlaceSelectionField({ + label, + onChange, + value, + multiple, + handlePlaceAdd, +}) { + const { selectedPlacesMap, savedPlacesMap } = useContext(GlobalContext); + return ( + <> +No API Available
+ ); +} diff --git a/src/components/GoogleMaps/Forms/RouteSearchForm.jsx b/src/components/GoogleMaps/Forms/RouteSearchForm.jsx new file mode 100644 index 0000000..6da499d --- /dev/null +++ b/src/components/GoogleMaps/Forms/RouteSearchForm.jsx @@ -0,0 +1,522 @@ +"use client"; + +import React, { useCallback, useEffect, useRef, useState } from "react"; +import mapApi from "@/api/mapApi"; +import { + TextField, + Grid, + FormControlLabel, + RadioGroup, + Radio, + Typography, + FormControl, + InputLabel, + Select, + MenuItem, + ListItemText, + Checkbox, + OutlinedInput, + Divider, + Box, + Paper, +} from "@mui/material"; +import types from "@/database/newtypes.json"; +import { LoadingButton } from "@mui/lab"; +import { Add, Search } from "@mui/icons-material"; +import PlaceSelectionField from "@/components/GoogleMaps/InputFields/PlaceSelectionField"; +import { GlobalContext } from "@/contexts/GlobalContext"; +import { useContext } from "react"; +import TypeSelectionField from "@/mapServices/GoogleMaps/TypeSelectionField"; +import { AppContext } from "@/contexts/AppContext"; +import { showError } from "@/contexts/ToastProvider"; +import { + GoogleMap, + LoadScript, + useJsApiLoader, + Marker, +} from "@react-google-maps/api"; +import PoiSelectionField from "../InputFields/PoiSelectionField"; +import TravelSelectionField from "../../../mapServices/GoogleMaps/TravelSelectionField"; +import RoutePlacesComponent from "../Embed/RoutePlacesComponent"; +import debounce from "lodash/debounce"; + +const placeTypes = []; +for (const category in types) { + placeTypes.push(...types[category]); +} +console.log(placeTypes); + +// PRICE_LEVEL_FREE is not allowed in a request. It is only used to populate the response. +const priceMap = { + PRICE_LEVEL_INEXPENSIVE: "Inexpensive", + PRICE_LEVEL_MODERATE: "Moderate", + PRICE_LEVEL_EXPENSIVE: "Expensive", + PRICE_LEVEL_VERY_EXPENSIVE: "Very Expensive", +}; +const avoidMap = { + avoidTolls: "Tolls", + avoidHighways: "Highways", + avoidFerries: "Ferries", +}; +export default function RouteSearchForm({ + handlePlaceAdd, + newRoutePlaces, + setNewRoutePlaces, +}) { + const { + selectedPlacesMap, + routePlacesMap, + setRoutePlacesMap, + setApiCallLogs, + savedPlacesMap, + setSavedPlacesMap, + tools, + } = useContext(GlobalContext); + + const [routes, setRoutes] = useState([]); + const [places, setPlaces] = useState([]); + const [apiCalls, setApiCalls] = useState([]); + const [uuid, setUuid] = useState(""); + const [loading, setLoading] = useState(false); + const [mapsApi, setMapsApi] = useState(null); + useEffect(() => { + const loadMapsApi = async () => { + const mapsModule = await import( + process.env.NEXT_PUBLIC_MAPS_API_PATH + ); + setMapsApi(mapsModule.default); + }; + + loadMapsApi(); + }, []); + + console.log( + "Destination", + newRoutePlaces.destination, + newRoutePlaces.destination + ? savedPlacesMap[newRoutePlaces.destination] + : "'" + ); + + const handleSave = async (place) => { + let details = savedPlacesMap[place.id]; + if (details === undefined) { + setSavedPlacesMap((prev) => ({ + ...prev, + [place.id]: { + ...place, + uuid, + mapService: tools.searchAlongRoute.family, + hidden: true, + }, + })); + } + }; + + const searchNearbyPlaces = async () => { + if (newRoutePlaces.type === "") return; + setLoading(true); + + // if (!placeTypes.includes(newRoutePlaces.type)) { + // newRoutePlaces.keyword = newRoutePlaces.type; + // newRoutePlaces.type = ""; + // } else { + // newRoutePlaces.keyword = ""; + // } + + if (routes.length > 0 && places.length > 0) { + const newRoutePlacesMap = [...routePlacesMap]; + newRoutePlacesMap.push({ + type: + newRoutePlaces.searchBy === "type" + ? newRoutePlaces.type + : newRoutePlaces.keyword, + minRating: newRoutePlaces.minRating, + priceLevels: newRoutePlaces.priceLevels, + rankPreference: newRoutePlaces.rankPreference, + routes: routes, + places: places, + origin: newRoutePlaces.origin, + destination: newRoutePlaces.destination, + travelMode: newRoutePlaces.travelMode, + routeModifiers: newRoutePlaces.routeModifiers, + maxResultCount: newRoutePlaces.maxResultCount, + uuid, + }); + + setRoutePlacesMap(newRoutePlacesMap); + setApiCallLogs((prev) => [...prev, ...apiCalls]); + setNewRoutePlaces((prev) => ({ + ...prev, + type: "", + keyword: "", + })); + setLoading(false); + + places.forEach((place) => { + handleSave(place); + }); + } else { + showError("Couldn't find nearby places."); + } + setLoading(false); + }; + + const compute = async (data) => { + if ( + data.origin === "" || + data.destination === "" || + data.travelMode === "" + ) { + return; + } + setLoading(true); + + if (data.type === "") { + const response = await tools.searchAlongRoute.run({ + ...data, + origin: savedPlacesMap[data.origin], + destination: savedPlacesMap[data.destination], + }); + if (response.success) { + setRoutes(response.data.route_response.routes); + setApiCalls(response.data.apiCallLogs); + setUuid(response.data.uuid); + } + } else { + const response = await tools.searchAlongRoute.run({ + ...data, + origin: savedPlacesMap[data.origin], + destination: savedPlacesMap[data.destination], + }); + if (response.success) { + setRoutes(response.data.route_response.routes); + setPlaces(response.data.nearby_response.places); + setApiCalls(response.data.apiCallLogs); + setUuid(response.data.uuid); + } + } + setLoading(false); + }; + + const debouncedCompute = useCallback(debounce(compute, 1000), [tools]); + + useEffect(() => { + debouncedCompute(newRoutePlaces); + }, [newRoutePlaces, debouncedCompute]); + + return newRoutePlaces && tools.searchAlongRoute ? ( +No API Available
+ ); +} diff --git a/src/components/GoogleMaps/Grids/DirectionGrid.jsx b/src/components/GoogleMaps/Grids/DirectionGrid.jsx new file mode 100644 index 0000000..72cbe71 --- /dev/null +++ b/src/components/GoogleMaps/Grids/DirectionGrid.jsx @@ -0,0 +1,29 @@ +import React, { useContext } from "react"; +import { Grid } from "@mui/material"; +import DirectionCard from "../Cards/DirectionCard"; + +export default function DirectionGrid({ + directionInformation, + setDirectionInformation, + savedPlacesMap, + mode, +}) { + return ( ++ {notFound + ? "No places found" + : "Press enter to search"} +
++ No places found +
++ {notFound + ? "No places found" + : "Press enter to search"} +
+No API Available
+ ); +} diff --git a/src/components/InputFields/CurrentLocationSelectionField.jsx b/src/components/GoogleMaps/InputFields/CurrentLocationSelectionField.jsx similarity index 88% rename from src/components/InputFields/CurrentLocationSelectionField.jsx rename to src/components/GoogleMaps/InputFields/CurrentLocationSelectionField.jsx index aaad041..aed9f63 100644 --- a/src/components/InputFields/CurrentLocationSelectionField.jsx +++ b/src/components/GoogleMaps/InputFields/CurrentLocationSelectionField.jsx @@ -12,8 +12,7 @@ export default function CurrentLocationSelectionField({ multiple, handlePlaceAdd, }) { - const { selectedPlacesMap } = useContext(GlobalContext); - const { savedPlacesMap } = useContext(AppContext); + const { selectedPlacesMap, savedPlacesMap } = useContext(GlobalContext); return ( <> diff --git a/src/components/GoogleMaps/InputFields/PlaceSelectionField.jsx b/src/components/GoogleMaps/InputFields/PlaceSelectionField.jsx new file mode 100644 index 0000000..7a63661 --- /dev/null +++ b/src/components/GoogleMaps/InputFields/PlaceSelectionField.jsx @@ -0,0 +1,115 @@ +/* eslint-disable @next/next/no-img-element */ +import React, { useContext } from "react"; +import FormControl from "@mui/material/FormControl"; +import InputLabel from "@mui/material/InputLabel"; +import { Select, MenuItem, Box, Chip, Typography } from "@mui/material"; +import { GlobalContext } from "@/contexts/GlobalContext"; +import { AppContext } from "@/contexts/AppContext"; +import mapServices from "@/tools/MapServices"; + +export default function PlaceSelectionField({ + label, + onChange, + value, + multiple, + handlePlaceAdd, +}) { + const { selectedPlacesMap, savedPlacesMap } = useContext(GlobalContext); + + return ( + <> +- {notFound - ? "No places found" - : "Press enter to search"} -
-- No places found -
-- {notFound - ? "No places found" - : "Press enter to search"} -
-