file: create client directory with files
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
> 1%
|
||||
last 2 versions
|
||||
not dead
|
||||
not ie 11
|
||||
@@ -0,0 +1,17 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: {
|
||||
node: true
|
||||
},
|
||||
'extends': [
|
||||
'plugin:vue/vue3-essential',
|
||||
'eslint:recommended'
|
||||
],
|
||||
parserOptions: {
|
||||
parser: '@babel/eslint-parser'
|
||||
},
|
||||
rules: {
|
||||
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
/dist
|
||||
|
||||
|
||||
# local env files
|
||||
.env
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# Log files
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
node_modules
|
||||
@@ -0,0 +1,24 @@
|
||||
# client
|
||||
|
||||
## Project setup
|
||||
```
|
||||
npm install
|
||||
```
|
||||
|
||||
### Compiles and hot-reloads for development
|
||||
```
|
||||
npm run serve
|
||||
```
|
||||
|
||||
### Compiles and minifies for production
|
||||
```
|
||||
npm run build
|
||||
```
|
||||
|
||||
### Lints and fixes files
|
||||
```
|
||||
npm run lint
|
||||
```
|
||||
|
||||
### Customize configuration
|
||||
See [Configuration Reference](https://cli.vuejs.org/config/).
|
||||
@@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
'@vue/cli-plugin-babel/preset'
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"module": "esnext",
|
||||
"baseUrl": "./",
|
||||
"moduleResolution": "node",
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"src/*"
|
||||
]
|
||||
},
|
||||
"lib": [
|
||||
"esnext",
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"scripthost"
|
||||
]
|
||||
}
|
||||
}
|
||||
Generated
+20505
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"name": "client",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build",
|
||||
"lint": "vue-cli-service lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.24.0",
|
||||
"core-js": "^3.8.3",
|
||||
"leaflet": "^1.7.1",
|
||||
"vue": "^3.2.13",
|
||||
"vue-router": "^4.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.12.16",
|
||||
"@babel/eslint-parser": "^7.12.16",
|
||||
"@vue/cli-plugin-babel": "~5.0.0-rc.1",
|
||||
"@vue/cli-plugin-eslint": "~5.0.0-rc.1",
|
||||
"@vue/cli-plugin-router": "~5.0.0-rc.1",
|
||||
"@vue/cli-service": "~5.0.0-rc.1",
|
||||
"autoprefixer": "^10.4.2",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-plugin-vue": "^8.0.3",
|
||||
"postcss": "^8.4.5",
|
||||
"tailwindcss": "^3.0.12"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
@@ -0,0 +1,32 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico" />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css"
|
||||
integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A=="
|
||||
crossorigin=""
|
||||
/>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css"
|
||||
crossorigin="anonymous"
|
||||
referrerpolicy="no-referrer"
|
||||
/>
|
||||
<title>Geocoding App</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
<strong
|
||||
>We're sorry but Geocoding App doesn't work properly without JavaScript
|
||||
enabled. Please enable it to continue.</strong
|
||||
>
|
||||
</noscript>
|
||||
<div id="app"></div>
|
||||
<!-- built files will be auto injected -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,4 @@
|
||||
<template>
|
||||
<nav></nav>
|
||||
<router-view />
|
||||
</template>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 6.7 KiB |
@@ -0,0 +1 @@
|
||||
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="map-marker-alt" class="svg-inline--fa fa-map-marker-alt fa-w-12" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path fill="#0000FF" d="M172.268 501.67C26.97 291.031 0 269.413 0 192 0 85.961 85.961 0 192 0s192 85.961 192 192c0 77.413-26.97 99.031-172.268 309.67-9.535 13.774-29.93 13.773-39.464 0zM192 272c44.183 0 80-35.817 80-80s-35.817-80-80-80-80 35.817-80 80 35.817 80 80 80z"></path></svg>
|
||||
|
After Width: | Height: | Size: 487 B |
@@ -0,0 +1 @@
|
||||
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="map-marker-alt" class="svg-inline--fa fa-map-marker-alt fa-w-12" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path fill="#FF0000" d="M172.268 501.67C26.97 291.031 0 269.413 0 192 0 85.961 85.961 0 192 0s192 85.961 192 192c0 77.413-26.97 99.031-172.268 309.67-9.535 13.774-29.93 13.773-39.464 0zM192 272c44.183 0 80-35.817 80-80s-35.817-80-80-80-80 35.817-80 80 35.817 80 80 80z"></path></svg>
|
||||
|
After Width: | Height: | Size: 487 B |
@@ -0,0 +1,3 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@@ -0,0 +1,21 @@
|
||||
<template>
|
||||
<div class="h-full w-full flex items-center justify-center">
|
||||
<span
|
||||
class="
|
||||
block
|
||||
w-[60px]
|
||||
h-[60px]
|
||||
border-[5px] border-solid border-transparent border-t-black
|
||||
rounded-full
|
||||
animate-spin
|
||||
"
|
||||
>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "loadingSpinner",
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<div
|
||||
class="
|
||||
h-screen
|
||||
absolute
|
||||
w-full
|
||||
z-10
|
||||
flex
|
||||
justify-center
|
||||
items-start
|
||||
pt-[125px]
|
||||
bg-black/50
|
||||
"
|
||||
>
|
||||
<div
|
||||
class="flex flex-col bg-white w-[80%] sm:w-[450px] px-6 py-4 rounded-md"
|
||||
>
|
||||
<h1 class="text-lg mb-1">Error: {{ this.geoErrorMsg }}</h1>
|
||||
<p class="text-sm mb-4">
|
||||
To take advantage of this applications features, please ensure location
|
||||
services are enabled
|
||||
</p>
|
||||
<button
|
||||
@click="$emit('closeGeoError')"
|
||||
class="self-start py-2 px-4 bg-red-500 text-white rounded-md text-sm"
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ["geoErrorMsg"],
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,59 @@
|
||||
<template>
|
||||
<div class="hello">
|
||||
<h1>{{ msg }}</h1>
|
||||
<p>
|
||||
For a guide and recipes on how to configure / customize this project,<br>
|
||||
check out the
|
||||
<a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
|
||||
</p>
|
||||
<h3>Installed CLI Plugins</h3>
|
||||
<ul>
|
||||
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
|
||||
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-router" target="_blank" rel="noopener">router</a></li>
|
||||
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
|
||||
</ul>
|
||||
<h3>Essential Links</h3>
|
||||
<ul>
|
||||
<li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
|
||||
<li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
|
||||
<li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
|
||||
<li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
|
||||
<li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
|
||||
</ul>
|
||||
<h3>Ecosystem</h3>
|
||||
<ul>
|
||||
<li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
|
||||
<li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
|
||||
<li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
|
||||
<li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
|
||||
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'HelloWorld',
|
||||
props: {
|
||||
msg: String
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style scoped>
|
||||
h3 {
|
||||
margin: 40px 0 0;
|
||||
}
|
||||
ul {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
}
|
||||
li {
|
||||
display: inline-block;
|
||||
margin: 0 10px;
|
||||
}
|
||||
a {
|
||||
color: #42b983;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,154 @@
|
||||
<template>
|
||||
<div
|
||||
class="
|
||||
w-full
|
||||
md:w-auto
|
||||
absolute
|
||||
md:top-[40px] md:left-[60px]
|
||||
z-[2]
|
||||
flex
|
||||
gap-4
|
||||
px-6
|
||||
py-8
|
||||
md:px-0 md:py-0
|
||||
bg-transparent
|
||||
"
|
||||
>
|
||||
<!-- Search -->
|
||||
<div class="relative flex-1 md:min-w-[350px]">
|
||||
<!-- Search Input -->
|
||||
<input
|
||||
class="
|
||||
pl-9
|
||||
pr-4
|
||||
py-3
|
||||
text-[14px]
|
||||
focus:outline-none
|
||||
w-full
|
||||
shadow-md
|
||||
rounded-md
|
||||
"
|
||||
type="text"
|
||||
placeholder="Start your search..."
|
||||
v-model="searchQuery"
|
||||
@input="search"
|
||||
@focus="$emit('toggleSearchResults')"
|
||||
/>
|
||||
<!-- Search Icon -->
|
||||
<div class="absolute top-0 left-[8px] h-full flex items-center">
|
||||
<i class="fas fa-search"></i>
|
||||
</div>
|
||||
<!-- Search Results -->
|
||||
<div class="absolute mt-[8px] w-full">
|
||||
<!-- Search Queries -->
|
||||
<div
|
||||
v-if="searchQuery && searchResults"
|
||||
class="h-[200px] overflow-scroll bg-white rounded-md"
|
||||
>
|
||||
<!-- Loading Spinner -->
|
||||
<BaseLoader v-if="!searchData" />
|
||||
<!-- Display Results -->
|
||||
<div v-else>
|
||||
<div
|
||||
class="
|
||||
px-4
|
||||
py-2
|
||||
flex
|
||||
gap-x-2
|
||||
cursor-pointer
|
||||
hover:bg-slate-600 hover:text-white
|
||||
"
|
||||
v-for="(result, index) in searchData"
|
||||
:key="index"
|
||||
@click="selectResult(result)"
|
||||
>
|
||||
<i class="fas fa-map-marker-alt"></i>
|
||||
<p class="text-[12px]">{{ result.place_name_en }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Selected Search Result -->
|
||||
<div
|
||||
v-if="selectedResult"
|
||||
class="mt-[8px] px-4 py-3 bg-white rounded-md"
|
||||
>
|
||||
<i
|
||||
@click="removeResult"
|
||||
class="flex justify-end far fa-times-circle"
|
||||
></i>
|
||||
<h1 class="text-lg">{{ selectedResult.text }}</h1>
|
||||
<p class="text-xs mb-1">
|
||||
{{ selectedResult.properties.address }}, {{ selectedResult.city }},
|
||||
{{ selectedResult.state }}
|
||||
</p>
|
||||
<p class="text-xs">{{ selectedResult.properties.category }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Geolocation -->
|
||||
<div
|
||||
class="px-4 bg-white flex items-center shadow-md rounded-md min-h-[45px]"
|
||||
@click="$emit('getGeolocation')"
|
||||
:class="{ 'bg-slate-600': coords }"
|
||||
>
|
||||
<i
|
||||
class="fas fa-location-arrow 'text-slate-600' text-[18px]"
|
||||
:class="{ 'text-white': coords, 'animate-pulse': fetchCoords }"
|
||||
></i>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref } from "vue";
|
||||
import axios from "axios";
|
||||
import BaseLoader from "./BaseLoader.vue";
|
||||
export default {
|
||||
props: ["fetchCoords", "coords", "searchResults"],
|
||||
components: { BaseLoader },
|
||||
setup(props, { emit }) {
|
||||
const searchQuery = ref(null);
|
||||
const searchData = ref(null);
|
||||
const queryTimeout = ref(null);
|
||||
const selectedResult = ref(null);
|
||||
const search = () => {
|
||||
clearTimeout(queryTimeout.value);
|
||||
// reset data on a new search
|
||||
searchData.value = null;
|
||||
queryTimeout.value = setTimeout(async () => {
|
||||
// Only make search, if there is value in query input
|
||||
if (searchQuery.value !== "") {
|
||||
const params = new URLSearchParams({
|
||||
fuzzyMatch: true,
|
||||
language: "en",
|
||||
limit: 10,
|
||||
proximity: props.coords
|
||||
? `${props.coords.lng},${props.coords.lat}`
|
||||
: "0,0",
|
||||
});
|
||||
const data = await axios.get(
|
||||
`api/search/${searchQuery.value}?${params}`
|
||||
);
|
||||
searchData.value = data.data.features;
|
||||
}
|
||||
}, 750);
|
||||
};
|
||||
const selectResult = (result) => {
|
||||
selectedResult.value = result;
|
||||
emit("plotResult", result.geometry);
|
||||
};
|
||||
const removeResult = () => {
|
||||
selectedResult.value = null;
|
||||
emit("removeResult");
|
||||
};
|
||||
return {
|
||||
searchQuery,
|
||||
search,
|
||||
searchData,
|
||||
selectResult,
|
||||
selectedResult,
|
||||
removeResult,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,6 @@
|
||||
import { createApp } from "vue";
|
||||
import App from "./App.vue";
|
||||
import router from "./router";
|
||||
import "./assets/tailwind.css";
|
||||
|
||||
createApp(App).use(router).mount("#app");
|
||||
@@ -0,0 +1,17 @@
|
||||
import { createRouter, createWebHistory } from "vue-router";
|
||||
import HomeView from "../views/HomeView.vue";
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: "/",
|
||||
name: "home",
|
||||
component: HomeView,
|
||||
},
|
||||
];
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(process.env.BASE_URL),
|
||||
routes,
|
||||
});
|
||||
|
||||
export default router;
|
||||
@@ -0,0 +1,187 @@
|
||||
<template>
|
||||
<div class="h-screen relative">
|
||||
<BaseModal
|
||||
@closeGeoError="closeGeoError"
|
||||
v-if="geoError"
|
||||
:geoErrorMsg="geoErrorMsg"
|
||||
/>
|
||||
<MapFeatures
|
||||
@getGeolocation="getGeolocation"
|
||||
@plotResult="plotResult"
|
||||
@toggleSearchResults="toggleSearchResults"
|
||||
@removeResult="removeResult"
|
||||
:coords="coords"
|
||||
:fetchCoords="fetchCoords"
|
||||
:searchResults="searchResults"
|
||||
/>
|
||||
<div id="map" class="h-full z-[1]"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import leaflet from "leaflet";
|
||||
import { onMounted, ref } from "vue";
|
||||
import BaseModal from "@/components/BaseModal.vue";
|
||||
import MapFeatures from "@/components/MapFeatures.vue";
|
||||
|
||||
export default {
|
||||
name: "HomeView",
|
||||
components: {
|
||||
BaseModal,
|
||||
MapFeatures,
|
||||
},
|
||||
setup() {
|
||||
let map;
|
||||
onMounted(() => {
|
||||
// init map
|
||||
map = leaflet.map("map").setView([47.60357, -122.329449], 10);
|
||||
|
||||
// add tile layer
|
||||
leaflet
|
||||
.tileLayer(
|
||||
`https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token=${process.env.VUE_APP_API_KEY}`,
|
||||
{
|
||||
attribution:
|
||||
'Map data © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, Imagery © <a href="https://www.mapbox.com/">Mapbox</a>',
|
||||
maxZoom: 18,
|
||||
id: "mapbox/streets-v11",
|
||||
tileSize: 512,
|
||||
zoomOffset: -1,
|
||||
accessToken: process.env.VUE_APP_API_KEY,
|
||||
}
|
||||
)
|
||||
.addTo(map);
|
||||
|
||||
map.on("moveend", () => {
|
||||
closeSearchResults();
|
||||
});
|
||||
|
||||
getGeolocation();
|
||||
});
|
||||
|
||||
const coords = ref(null);
|
||||
const fetchCoords = ref(null);
|
||||
const geoMarker = ref(null);
|
||||
const geoError = ref(null);
|
||||
const geoErrorMsg = ref(null);
|
||||
|
||||
const getGeolocation = () => {
|
||||
if (!coords.value) {
|
||||
// check session storage for coords
|
||||
if (sessionStorage.getItem("coords")) {
|
||||
coords.value = JSON.parse(sessionStorage.getItem("coords"));
|
||||
plotGeoLocation(coords.value);
|
||||
return;
|
||||
}
|
||||
|
||||
fetchCoords.value = true;
|
||||
navigator.geolocation.getCurrentPosition(setCoords, getLocError);
|
||||
}
|
||||
|
||||
coords.value = null;
|
||||
sessionStorage.removeItem("coords");
|
||||
map.removeLayer(geoMarker.value);
|
||||
return;
|
||||
};
|
||||
|
||||
const setCoords = (pos) => {
|
||||
// stop fetching coords
|
||||
fetchCoords.value = null;
|
||||
|
||||
// set coords in session storage
|
||||
const setSessionCoords = {
|
||||
lat: pos.coords.latitude,
|
||||
lng: pos.coords.longitude,
|
||||
};
|
||||
|
||||
sessionStorage.setItem("coords", JSON.stringify(setSessionCoords));
|
||||
|
||||
// set ref coords value
|
||||
coords.value = setSessionCoords;
|
||||
|
||||
plotGeoLocation(coords.value);
|
||||
};
|
||||
|
||||
const getLocError = (err) => {
|
||||
fetchCoords.value = null;
|
||||
geoError.value = true;
|
||||
geoErrorMsg.value = err.message;
|
||||
};
|
||||
|
||||
const plotGeoLocation = (coords) => {
|
||||
// create custom marker
|
||||
const customMarker = leaflet.icon({
|
||||
iconUrl: require("../assets/map-marker-red.svg"),
|
||||
iconSize: [35, 35],
|
||||
});
|
||||
|
||||
// create new marker with coords and custom icon
|
||||
geoMarker.value = leaflet
|
||||
.marker([coords.lat, coords.lng], {
|
||||
icon: customMarker,
|
||||
})
|
||||
.addTo(map);
|
||||
|
||||
// setting map view to current location
|
||||
map.setView([coords.lat, coords.lng], 10);
|
||||
};
|
||||
|
||||
const closeGeoError = () => {
|
||||
geoError.value = null;
|
||||
geoErrorMsg.value = null;
|
||||
};
|
||||
|
||||
const resultMarker = ref(null);
|
||||
const plotResult = (coords) => {
|
||||
// Check to see if resultMarker has value
|
||||
if (resultMarker.value) {
|
||||
map.removeLayer(resultMarker.value);
|
||||
}
|
||||
|
||||
// create custom marker
|
||||
const customMarker = leaflet.icon({
|
||||
iconUrl: require("../assets/map-marker-blue.svg"),
|
||||
iconSize: [35, 35],
|
||||
});
|
||||
|
||||
// create new marker with coords and custom icon
|
||||
resultMarker.value = leaflet
|
||||
.marker([coords.coordinates[1], coords.coordinates[0]], {
|
||||
icon: customMarker,
|
||||
})
|
||||
.addTo(map);
|
||||
|
||||
// setting map view to current location
|
||||
map.setView([coords.coordinates[1], coords.coordinates[0]], 14);
|
||||
};
|
||||
|
||||
const searchResults = ref(null);
|
||||
const toggleSearchResults = () => {
|
||||
searchResults.value = !searchResults.value;
|
||||
};
|
||||
|
||||
const closeSearchResults = () => {
|
||||
searchResults.value = null;
|
||||
};
|
||||
|
||||
const removeResult = () => {
|
||||
map.removeLayer(resultMarker.value);
|
||||
};
|
||||
|
||||
return {
|
||||
coords,
|
||||
fetchCoords,
|
||||
geoMarker,
|
||||
closeGeoError,
|
||||
geoError,
|
||||
geoErrorMsg,
|
||||
getGeolocation,
|
||||
plotResult,
|
||||
searchResults,
|
||||
toggleSearchResults,
|
||||
closeSearchResults,
|
||||
removeResult,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,8 @@
|
||||
module.exports = {
|
||||
content: ["./index.html", "./src/**/*.{vue,js,ts,jsx,tsx}"],
|
||||
jit: true,
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
};
|
||||
@@ -0,0 +1,13 @@
|
||||
const { defineConfig } = require("@vue/cli-service");
|
||||
const path = require("path");
|
||||
module.exports = defineConfig({
|
||||
transpileDependencies: true,
|
||||
outputDir: path.resolve(__dirname, "../server/public"),
|
||||
devServer: {
|
||||
proxy: {
|
||||
"/api": {
|
||||
target: "http://localhost:3000",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user