1 Commits

Author SHA1 Message Date
3cb5a30270 feat: use drink api in front and more (#16)
All checks were successful
Build Docker Image Front / run (push) Successful in 28s
Build Docker Image Back / run (push) Successful in 27s
Reviewed-on: #16
Co-authored-by: Clement <c.boesmier@aptatio.com>
Co-committed-by: Clement <c.boesmier@aptatio.com>
2024-06-07 19:09:57 +02:00
16 changed files with 477 additions and 101 deletions

View File

@ -1,5 +1,8 @@
/** @type {import('ts-jest').JestConfigWithTsJest} */ /** @type {import('ts-jest').JestConfigWithTsJest} */
module.exports = { module.exports = {
testPathIgnorePatterns: [
"<rootDir>/dist/"
],
testTimeout: 10000, testTimeout: 10000,
preset: 'ts-jest', preset: 'ts-jest',
testEnvironment: 'node', testEnvironment: 'node',

View File

@ -13,7 +13,7 @@ const key = process.env.OPEN_TRIP_MAPS_KEY
* @param {string} lat2 Latitude of the 2nd point of the box * @param {string} lat2 Latitude of the 2nd point of the box
* @returns {FeatureCollection} a list of POIs with their type, id, etc. (cf: [opentripmap](https://dev.opentripmap.org/docs#)) * @returns {FeatureCollection} a list of POIs with their type, id, etc. (cf: [opentripmap](https://dev.opentripmap.org/docs#))
*/ */
async function callBox(lon1:string, lat1:string, lon2: string, lat2: string) { async function callBox(lon1:string, lat1:string, lon2: string, lat2: string, rate: string) {
const lonMin = Math.min(parseFloat(lon1), parseFloat(lon2)) const lonMin = Math.min(parseFloat(lon1), parseFloat(lon2))
const lonMax = Math.max(parseFloat(lon1), parseFloat(lon2)) const lonMax = Math.max(parseFloat(lon1), parseFloat(lon2))
const latMin = Math.min(parseFloat(lat1), parseFloat(lat2)) const latMin = Math.min(parseFloat(lat1), parseFloat(lat2))
@ -27,6 +27,7 @@ async function callBox(lon1:string, lat1:string, lon2: string, lat2: string) {
lon_max: lonMax, lon_max: lonMax,
lat_min: latMin, lat_min: latMin,
lat_max: latMax, lat_max: latMax,
rate: rate,
apikey: key, apikey: key,
kinds: 'bars,cafes,pubs,biergartens' kinds: 'bars,cafes,pubs,biergartens'
}, },
@ -135,9 +136,8 @@ export async function getCity(req: express.Request, res: express.Response) {
radius = "1000" radius = "1000"
} }
const cityPose = await callCity(cityName as string) const cityPose = await callCity(cityName as string)
const poi = await callRadius(cityPose.lon,cityPose.lat, radius as string) res.send( await callRadius(cityPose.lon,cityPose.lat, radius as string))
res.send( {...poi, ...cityPose}) }
}//XXX: refaire test avec city
/** /**
* Handle GET request for radius search route ('/otm/radius'). * Handle GET request for radius search route ('/otm/radius').
@ -183,11 +183,16 @@ export async function getBox(req:express.Request, res: express.Response) {
const lat1 = req.query["lat1"] as string const lat1 = req.query["lat1"] as string
const lon2 = req.query["lon2"] as string const lon2 = req.query["lon2"] as string
const lat2 = req.query["lat2"] as string const lat2 = req.query["lat2"] as string
let rate = req.query["rate"] as string
if(!lon1 || !lat1 || !lon2 || !lat2){ if(!lon1 || !lat1 || !lon2 || !lat2){
res.status(400).send("Missing Argument") res.status(400).send("Missing Argument")
return return
} }
res.send( await callBox(lon1, lat1, lon2, lat2)) if(!rate){
rate = "1";
}
res.send( await callBox(lon1, lat1, lon2, lat2, rate))
} }

View File

@ -219,6 +219,14 @@ describe("Test the otm city path", () => {
} }
] ]
} }
function sleep(milliseconds: number) {
const date = Date.now();
let currentDate = null;
do {
currentDate = Date.now();
} while (currentDate - date < milliseconds);
}
sleep(2000);
request(app) request(app)
.get("/otm/box") .get("/otm/box")
.query({'lon1':'-1.435199','lon2':'-1.43519', 'lat1':'46.668460', 'lat2':'46.668461'}) .query({'lon1':'-1.435199','lon2':'-1.43519', 'lat1':'46.668460', 'lat2':'46.668461'})

View File

@ -0,0 +1,18 @@
{
"folders": [
{
"name": "Ratrapage_WEB",
"path": "."
},
{
"name": "barAndCafe",
"path": "Express/barAndCafe"
},
{
"path": "front"
}
],
"settings": {
"typescript.tsdk": "node_modules/typescript/lib"
}
}

View File

@ -1,3 +1,8 @@
vars { vars {
EXPRESS_API: https://drink-tweb.cb85.fr EXPRESS_API: https://drink-tweb.cb85.fr
PB_URL: https://pb-tweb.cb85.fr
TRIPMAP_URL: https://api.opentripmap.com/0.1/
} }
vars:secret [
OTM_KEY
]

View File

@ -0,0 +1,18 @@
meta {
name: connexion
type: http
seq: 1
}
post {
url: {{PB_URL}}/api/collections/users/auth-with-password
body: json
auth: none
}
body:json {
{
"identity": "michel.biche@aptatio.com",
"password": "123456789"
}
}

View File

@ -0,0 +1,27 @@
meta {
name: get all poi
type: http
seq: 5
}
get {
url: {{PB_URL}}/api/collections/user_poi/records?filter=owner='vvy93m1hoaeshwy'&&poi_list='cabkkovmjsfoapa'
body: json
auth: bearer
}
query {
filter: owner
poi_list: 'cabkkovmjsfoapa'
}
auth:bearer {
token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb2xsZWN0aW9uSWQiOiJfcGJfdXNlcnNfYXV0aF8iLCJleHAiOjE3MTg4Mjg0NzEsImlkIjoidnZ5OTNtMWhvYWVzaHd5IiwidHlwZSI6ImF1dGhSZWNvcmQifQ.pC7u-QaZ_BYqWA5wG8wu1lRbbd4mKuKeAveWe_IBnfU
}
body:json {
{
"owner": "vvy93m1hoaeshwy",
"poi_list": "sh430u0im37cxm5"
}
}

View File

@ -0,0 +1,22 @@
meta {
name: link user to poi
type: http
seq: 3
}
post {
url: {{PB_URL}}/api/collections/user_poi/records
body: json
auth: bearer
}
auth:bearer {
token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb2xsZWN0aW9uSWQiOiJfcGJfdXNlcnNfYXV0aF8iLCJleHAiOjE3MTg4Mjg0NzEsImlkIjoidnZ5OTNtMWhvYWVzaHd5IiwidHlwZSI6ImF1dGhSZWNvcmQifQ.pC7u-QaZ_BYqWA5wG8wu1lRbbd4mKuKeAveWe_IBnfU
}
body:json {
{
"owner": "vvy93m1hoaeshwy",
"poi_list": "sh430u0im37cxm5"
}
}

View File

@ -0,0 +1,40 @@
meta {
name: send a poi
type: http
seq: 2
}
post {
url: {{PB_URL}}/api/collections/POI/records
body: json
auth: bearer
}
auth:bearer {
token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb2xsZWN0aW9uSWQiOiJfcGJfdXNlcnNfYXV0aF8iLCJleHAiOjE3MTg4Mjg0NzEsImlkIjoidnZ5OTNtMWhvYWVzaHd5IiwidHlwZSI6ImF1dGhSZWNvcmQifQ.pC7u-QaZ_BYqWA5wG8wu1lRbbd4mKuKeAveWe_IBnfU
}
body:json {
{
"Poi_id": "11472887",
"Poi": {
"type": "Feature",
"id": "11472887",
"geometry": {
"type": "Point",
"coordinates": [
-0.0626024,
51.4924088
]
},
"properties": {
"xid": "W544344833",
"name": "The Blue Anchor",
"rate": 2,
"osm": "way/544344833",
"wikidata": "Q7718716",
"kinds": "pubs,foods,shops,marketplaces,tourist_facilities"
}
}
}
}

View File

@ -0,0 +1,37 @@
meta {
name: send fav to astro
type: http
seq: 4
}
post {
url: http://localhost:3000/maps/save_poi
body: json
auth: bearer
}
auth:bearer {
token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb2xsZWN0aW9uSWQiOiJfcGJfdXNlcnNfYXV0aF8iLCJleHAiOjE3MTg4ODY2OTIsImlkIjoidnZ5OTNtMWhvYWVzaHd5IiwidHlwZSI6ImF1dGhSZWNvcmQifQ.R9PBGlHn6aBHt89g6G0NykMh_Vye24OpKEUYtz6R6Og
}
body:json {
{
"type": "Feature",
"id": "11472888",
"geometry": {
"type": "Point",
"coordinates": [
-0.0626024,
51.4924088
]
},
"properties": {
"xid": "W544344833",
"name": "The Blue Anchor",
"rate": 2,
"osm": "way/544344833",
"wikidata": "Q7718716",
"kinds": "pubs,foods,shops,marketplaces,tourist_facilities"
}
}
}

View File

@ -0,0 +1,30 @@
---
import type { Input as Props } from 'types';
const {value, checked ,name, label, autocomplete, placeholder, divClass, inputClass} = Astro.props;
---
<>
{
name && (
<div class={"flex flex-row items-center " + divClass}>
<input
type="checkbox"
name={name}
id={name}
checked={checked}
value={value}
autocomplete={autocomplete}
placeholder={placeholder}
class={"mr-2 size-5 " + inputClass}
/>
{label && (
<label for={name} class="block text-lg font-medium">
{label}
</label>
)}
</div>
)
}
</>

View File

@ -0,0 +1,19 @@
---
export interface Props {
values: Array<{label:String, name:string, checked?:boolean | undefined}>
}
const fields = Astro.props.values
---
<>
<div class="flex, flex-col">
{fields.map(value => (
<input type="radio" id={value.name} name="note-min" value={value.name} checked={value.checked}>
<label for={value.name} class="ml-1">{value.label}</label><br>
))}
</div>
</>

View File

@ -18,19 +18,19 @@ const metadata = {
<div class="w-full h-96 grow" id="map" /> <div class="w-full h-96 grow" id="map" />
<Button id="test-btn">test</Button> <Button id="test-btn">test</Button>
</div> </div>
<!-- TODO: faire en sort que le style soit propre -->
<!-- for remouve footer --> <!-- for remouve footer -->
<!-- <div slot="footer"></div> --> <!-- <div slot="footer"></div> -->
<!-- penser a rm 11 au rem au dessus pour la taille --> <!-- penser a rm 11 au rem au dessus pour la taille -->
</Layout> </Layout>
<script> <script>
import L from 'leaflet' import * as L from 'leaflet'
import markerShadow from "leaflet/dist/images/marker-shadow.png" import markerShadow from "leaflet/dist/images/marker-shadow.png"
import markerIcon from "leaflet/dist/images/marker-icon.png" import markerIcon from "leaflet/dist/images/marker-icon.png"
import 'leaflet-routing-machine/dist/leaflet-routing-machine.js' import 'leaflet-routing-machine/dist/leaflet-routing-machine.js'
import 'leaflet-control-geocoder/dist/Control.Geocoder.js' import 'leaflet-control-geocoder/dist/Control.Geocoder.js'
const icon = {icon: new L.Icon({iconUrl: markerIcon.src, shadowUrl: markerShadow.src, iconAnchor: [13,41]})} const icon = {icon: new L.Icon({iconUrl: markerIcon.src, shadowUrl: markerShadow.src, iconAnchor: [13,41]})}
// declare map // declare map
@ -57,7 +57,9 @@ const metadata = {
// L.latLng(47.218536, -1.554075) // L.latLng(47.218536, -1.554075)
// ], // ],
routeWhileDragging: true, routeWhileDragging: true,
//@ts-ignore
geocoder: L.Control.Geocoder.nominatim(), geocoder: L.Control.Geocoder.nominatim(),
//@ts-ignore
position: 'topleft', position: 'topleft',
showAlternatives: true, showAlternatives: true,
reverseWaypoints: true, reverseWaypoints: true,
@ -77,7 +79,6 @@ const metadata = {
console.log("route trouvé") console.log("route trouvé")
console.log(e) console.log(e)
}) })
const pouet = "pouet"
document.querySelector<HTMLButtonElement>('#test-btn')?.addEventListener('click', () => { document.querySelector<HTMLButtonElement>('#test-btn')?.addEventListener('click', () => {
routing.setWaypoints( routing.setWaypoints(
[L.latLng(46.6705431, -1.4269698),L.latLng(47.218536, -1.554075)] [L.latLng(46.6705431, -1.4269698),L.latLng(47.218536, -1.554075)]

View File

@ -2,45 +2,110 @@
import Layout from 'layouts/PageLayout.astro' import Layout from 'layouts/PageLayout.astro'
import 'leaflet/dist/leaflet.css' import 'leaflet/dist/leaflet.css'
import 'leaflet-geosearch/dist/geosearch.css' import 'leaflet-geosearch/dist/geosearch.css'
import 'leaflet-routing-machine/dist/leaflet-routing-machine.css' import CheckBox from 'components/CheckBox.astro'
import FormContainer from 'components/ui/Form.astro' import Radios from 'components/Radios.astro'
import Input from 'components/Input.astro' import ListResult, { type RecordModel } from 'pocketbase'
import Button from 'components/ui/Button.astro' import { Expand, Trash } from 'lucide-astro'
const pb = Astro.locals.pb
const connected = pb.authStore.isValid
const metadata = { const metadata = {
title: 'Maps', title: 'Maps',
ignoreTitleTemplate: true, ignoreTitleTemplate: true,
} }
let fav = new Array<any>
if(connected){
try {
const request = await pb.collection('user_poi').getList(1,10,{filter:`owner="${pb.authStore.model!.id}"`, expand:"poi_list"})
request.items.forEach(element => {
fav.push(element.expand!.poi_list.Poi)
});
} catch (error) {
console.log(error);
}
}
console.log(fav);
--- ---
<Layout metadata={metadata}> <Layout metadata={metadata}>
<div class="h-[calc(100vh-16rem)] flex flex-col"> <div class="h-[calc(100vh-5rem)] flex flex-row">
<div class="w-full h-96 grow" id="map" /> <div class="w-1/5 flex flex-col">
<p id="message" class="hidden text-center mb-3">pouet</p>
<p class="text-center text-2xl mb-3">Filtre :</p>
<div class="">
<p>note minimal :</p>
<Radios
values={[
{name: "1", label: "1", checked: true},
{name: "2", label: "2"},
{name: "3", label: "3"},
{name: "1h", label: "1h"},
{name: "2h", label: "2h"},
{name: "3h", label: "3h"},
]}
/>
<p class="text-center text-xl mb-3">Type POI :</p>
<CheckBox
label="Bar, Pub, Café,..."
name="filter"
value="drink"
checked
/>
</div>
<div class="mb-3 grow">
<p class="text-center text-xl mb-3">Source :</p>
<CheckBox
label="Open Trip Maps"
name="filter"
value="otm"
checked
/>
</div>
{connected && (
<div class="mb-2">
<p class="text-center text-xl mb-3">Favori :</p>
{fav.map(val =>(
<div class="ml-2 flex flex-row mb-1" id="fav-div">
<p class="grow poi-favori" id={val.id} data-pos={val.geometry.coordinates}>{val.properties.name}</p>
<Trash id={val.id} class="poi-trash"/>
</div>
))}
</div>
)}
</div>
<div class="flex flex-col h-full grow">
<div class="w-full h-full grow" id="map" />
</div>
</div> </div>
<!-- TODO: faire en sort que le style soit propre -->
<script src='leaflet-routing-machine/dist/leaflet-routing-machine.js'></script>
<!-- for remouve footer --> <!-- for remouve footer -->
<!-- <div slot="footer"></div> --> <div slot="footer"></div>
<!-- penser a rm 11 au rem au dessus pour la taille --> <!-- penser a rm 11 au rem au dessus pour la taille -->
</Layout> </Layout>
<script> <script>
import L, { geoJSON, Icon, Popup, type LatLngTuple } from 'leaflet' import L, {Popup} from 'leaflet'
import markerShadow from "leaflet/dist/images/marker-shadow.png" import markerShadow from "leaflet/dist/images/marker-shadow.png"
import markerIcon from "leaflet/dist/images/marker-icon.png" import markerIcon from "leaflet/dist/images/marker-icon.png"
import { OpenStreetMapProvider } from 'leaflet-geosearch' import { OpenStreetMapProvider } from 'leaflet-geosearch'
import { GeoSearchControl } from 'leaflet-geosearch' import { GeoSearchControl } from 'leaflet-geosearch'
import 'leaflet-routing-machine/dist/leaflet-routing-machine.js'
import 'leaflet-control-geocoder/dist/Control.Geocoder.js' const showFav = document.querySelectorAll(".poi-favori")
const trashFav = document.querySelectorAll(".poi-trash")
const favDiv = document.querySelector('#fav-div')
const icon = {icon: new L.Icon({iconUrl: markerIcon.src, shadowUrl: markerShadow.src, iconAnchor: [13,41]})} const icon = {icon: new L.Icon({iconUrl: markerIcon.src, shadowUrl: markerShadow.src, iconAnchor: [13,41]})}
// const BACK_URL = "https://drink-tweb.cb85.fr/" // const BACK_URL = "http://localhost:3001/" //XXX : mettre url de prod
const BACK_URL = "http://localhost:3001/" const BACK_URL = "https://drink-tweb.cb85.fr/" //XXX : mettre url de prod
let mapsCenter : L.LatLngTuple
// declare map // declare map
const map = L.map('map', { const map = L.map('map', {
@ -57,8 +122,7 @@ const metadata = {
// move zoom ctl to bottom // move zoom ctl to bottom
L.control.zoom({ L.control.zoom({
position: 'bottomleft' position: 'bottomleft'
}).addTo(map); }).addTo(map)
const provider = new OpenStreetMapProvider() const provider = new OpenStreetMapProvider()
@ -71,64 +135,89 @@ const metadata = {
}), }),
) )
L.Routing.control({ async function saveToFav(element: any, save: boolean){
routeWhileDragging: true, const url = '/maps/save_poi';
geocoder: L.Control.Geocoder.nominatim(), const options = {
position: 'topleft', method: 'POST',
showAlternatives: true, body: JSON.stringify({Poi : element, save: save})
reverseWaypoints: true, };
routeWhileDragging: true,
altLineOptions: {
missingRouteTolerance: 50,
extendToWaypoints: true,
styles: [
{color: 'black', opacity: 0.15, weight: 9},
{color: 'white', opacity: 0.2, weight: 6},
{color: 'blue', opacity: 5, weight: 2}
]
}
}).addTo(map).on('routeselected', (e) => {
console.log(e)
}).on('routesfound', () =>{
console.log("test")
})
try {
const response = await fetch(url, options);
const data = await response.json();
console.log(data);
} catch (error) {
console.error(error);
}
}
function eventGotoFav(e: Element){
e.addEventListener('click', () =>{
const pose = e.getAttribute('data-pos')?.split(',')
map.panTo(new L.LatLng(parseFloat(pose![1]),parseFloat(pose![0])))
})
}
function eventRemoveFav(e: Element){
e.addEventListener('click', async () => {
await saveToFav({id:e.id, type:"Feature"},false)
document.location.reload()
})
}
showFav.forEach(e => {eventGotoFav(e)})
trashFav.forEach(e => {eventRemoveFav(e)})
let poiMarkers = new Array<L.Marker> let poiMarkers = new Array<L.Marker>
let minimalNote = "1"
let drink = true
let otm = true
// run api search // run api search
function searchBox(){ function searchBox(){
const nordWest = map.getBounds().getNorthWest() const nordWest = map.getBounds().getNorthWest()
const southEast = map.getBounds().getSouthEast() const southEast = map.getBounds().getSouthEast()
const params: URLSearchParams = new URLSearchParams(); const params: URLSearchParams = new URLSearchParams()
params.append("lon1", nordWest.lng.toString()) params.append("lon1", nordWest.lng.toString())
params.append("lat1", nordWest.lat.toString()) params.append("lat1", nordWest.lat.toString())
params.append("lon2", southEast.lng.toString()) params.append("lon2", southEast.lng.toString())
params.append("lat2", southEast.lat.toString()) params.append("lat2", southEast.lat.toString())
params.append("rate", minimalNote)
fetch(`${BACK_URL}otm/box?${params.toString()}`,{method: 'GET',headers: {'Content-Type': 'application/json'}}).then(function (response) {
return response.json(); if(drink && otm){
}).then(function (data) { fetch(`${BACK_URL}otm/box?${params.toString()}`,{method: 'GET',headers: {'Content-Type': 'application/json'}}).then(function (response) {
poiMarkers.forEach(element => { return response.json()
element.remove(); }).then(function (data) {
}); poiMarkers.forEach(element => {
data.features.forEach(element => { element.remove()
const prop = element.properties })
const popup: Popup = new Popup() console.log(data)
let tags = new String() data.features.forEach(element => {
prop.kinds.split(",").forEach(element => { const prop = element.properties
tags += "- " + element + "<br/>" const popup: Popup = new Popup()
}); let tags = new String()
const poiMarker = L.marker([element.geometry.coordinates[1],element.geometry.coordinates[0]],icon) prop.kinds.split(",").forEach(element => {
.bindPopup(`<b>${prop.name}</b><br/>note : ${prop.rate} <br/>tags:<br/> ${tags}`) tags += "- " + element + "<br/>"
poiMarker.addTo(map) })
poiMarkers.push(poiMarker); const poiMarker = L.marker([element.geometry.coordinates[1],element.geometry.coordinates[0]],icon)
}); .bindPopup(`<b>${prop.name}</b><br/>note : ${prop.rate} <br/>tags:<br/> ${tags} <p>favori : <input type="checkbox" name="like" id=${element.id}/><p>`)
}).catch(function (err) { .on("click", () => {
console.warn('Something went wrong.', err); document.querySelectorAll<HTMLInputElement>('input[name="like"]').forEach(e => {
}); e.addEventListener("click", () => {
console.log(element)
saveToFav(element, e.checked)
document.location.reload()
})
})
})
poiMarker.addTo(map)
poiMarkers.push(poiMarker)
})
}).catch(function (err) {
console.warn('Something went wrong.', err)
})
}
} }
// fonciton pour lancer la recherche de box sur l'api // fonciton pour lancer la recherche de box sur l'api
@ -136,52 +225,57 @@ const metadata = {
if(map.getZoom() >= 13){ if(map.getZoom() >= 13){
console.log("zoom OKAY") console.log("zoom OKAY")
//TODO: mettre un message de recherche en cour //TODO: mettre un message de recherche en cour
searchBox(); searchBox()
}else{ }else{
console.log("zoom more to see result"); console.log("zoom more to see result")
} }
} }
// envent pour lancer la recherche // envent pour lancer la recherche
const cooldown = 1000; const cooldown = 400
sender() sender()
let timeoutHandle = window.setTimeout(sender, cooldown) let timeoutHandle = window.setTimeout(sender, cooldown)
window.clearTimeout(timeoutHandle); window.clearTimeout(timeoutHandle)
map.addEventListener("move",() =>{ map.addEventListener("move",() =>{
window.clearTimeout(timeoutHandle); window.clearTimeout(timeoutHandle)
timeoutHandle = window.setTimeout(sender, cooldown); timeoutHandle = window.setTimeout(sender, cooldown)
}) })
map.addEventListener("zoom", () => { map.addEventListener("zoom", () => {
if(map.getZoom() <= 11){ if(map.getZoom() <= 11){
poiMarkers.forEach(element => { poiMarkers.forEach(element => {
element.remove() element.remove()
}); })
} }
}) })
function search() { document.querySelectorAll<HTMLInputElement>("input[name='filter']").forEach(e =>{
const params: URLSearchParams = new URLSearchParams(); e.addEventListener("click", () => {
params.append("name", input!.value) switch (e.value) {
case "drink":
drink = e.checked
break;
case "otm":
otm = e.checked
break;
fetch(`${BACK_URL}otm/city?${params.toString()}`,{method: 'GET',headers: {'Content-Type': 'application/json'}}).then(function (response) { default:
// The API call was successful! break;
return response.json(); }
}).then(function (data) { poiMarkers.forEach(element => {
console.log(data); element.remove()
mapsCenter = [data.lat, data.lon] })
marker.setLatLng(mapsCenter) searchBox()
map.setView(mapsCenter) })
data.features.forEach(element => { })
const prop = element.properties
const popup: Popup = new Popup() document.querySelectorAll<HTMLInputElement>('input[name="note-min"]').forEach(e => {
const poiMarker = L.marker([element.geometry.coordinates[1],element.geometry.coordinates[0]],icon).bindPopup(prop.name + "\n" + prop.rate + "\n" + prop.kinds) e.addEventListener("click", () => {
poiMarker.addTo(map) minimalNote = e.value
}); searchBox()
}).catch(function (err) { })
console.warn('Something went wrong.', err); })
});
}
</script> </script>

View File

@ -0,0 +1,47 @@
---
import AstroUtils from "libs/AstroUtils";
import type { RecordModel } from "pocketbase";
const pb = Astro.locals.pb
if(!pb.authStore.isValid){
return Astro.redirect("/account")
}
await AstroUtils.wrap(async () => {
if (Astro.request.method !== 'POST') {
return
}
const poiJson = await Astro.request.json()
const data = {Poi: poiJson.Poi, Poi_id:poiJson.Poi.id}
let record: RecordModel
try {
record = await pb.collection('POI').create(data);
} catch (error) {
try{
record = await pb.collection('POI').getFirstListItem(`Poi_id="${poiJson.Poi.id}"`)
} catch (error2) {
console.log("error 1 :")
console.log(error)
console.log("error 2 :")
console.log(error2)
}
}
try {
if(poiJson.save){
await pb.collection('user_poi').create({owner: pb.authStore.model!.id, poi_list:record!.id})
}else{
record = await pb.collection('user_poi').getFirstListItem(`poi_list="${record!.id}"&&owner="${pb.authStore.model!.id}"`)
await pb.collection('user_poi').delete(record.id)
}
} catch (error) {
console.log(error)
}
})
---

View File

@ -162,13 +162,15 @@ export interface Testimonial {
} }
export interface Input { export interface Input {
type: HTMLInputTypeAttribute type?: HTMLInputTypeAttribute
name: string name: string
label?: string label?: string
autocomplete?: string autocomplete?: string
placeholder?: string placeholder?: string
divClass?: string divClass?: string
inputClass?: string inputClass?: string
checked?: bool
value?:string
} }
export interface Textarea { export interface Textarea {