Refactor search results with dedicated route
This commit is contained in:
+26
-74
@@ -21,6 +21,7 @@ export default class MapComponent extends Component {
|
|||||||
@service osm;
|
@service osm;
|
||||||
@service storage;
|
@service storage;
|
||||||
@service mapUi;
|
@service mapUi;
|
||||||
|
@service router;
|
||||||
|
|
||||||
mapInstance;
|
mapInstance;
|
||||||
bookmarkSource;
|
bookmarkSource;
|
||||||
@@ -510,6 +511,17 @@ export default class MapComponent extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sync the pulse animation with the UI service state
|
||||||
|
syncPulse = modifier(() => {
|
||||||
|
if (!this.searchOverlayElement) return;
|
||||||
|
|
||||||
|
if (this.mapUi.isSearching) {
|
||||||
|
this.searchOverlayElement.classList.add('active');
|
||||||
|
} else {
|
||||||
|
this.searchOverlayElement.classList.remove('active');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
handleMapMove = async () => {
|
handleMapMove = async () => {
|
||||||
if (!this.mapInstance) return;
|
if (!this.mapInstance) return;
|
||||||
|
|
||||||
@@ -546,7 +558,6 @@ export default class MapComponent extends Component {
|
|||||||
});
|
});
|
||||||
let clickedBookmark = null;
|
let clickedBookmark = null;
|
||||||
let selectedFeatureName = null;
|
let selectedFeatureName = null;
|
||||||
let selectedFeatureType = null;
|
|
||||||
|
|
||||||
if (features && features.length > 0) {
|
if (features && features.length > 0) {
|
||||||
console.debug(`Found ${features.length} features in map layer:`);
|
console.debug(`Found ${features.length} features in map layer:`);
|
||||||
@@ -561,7 +572,6 @@ export default class MapComponent extends Component {
|
|||||||
const props = features[0].getProperties();
|
const props = features[0].getProperties();
|
||||||
if (props.name) {
|
if (props.name) {
|
||||||
selectedFeatureName = props.name;
|
selectedFeatureName = props.name;
|
||||||
selectedFeatureType = props.class || props.subclass;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -573,9 +583,7 @@ export default class MapComponent extends Component {
|
|||||||
'Clicked bookmark while sidebar open (switching):',
|
'Clicked bookmark while sidebar open (switching):',
|
||||||
clickedBookmark
|
clickedBookmark
|
||||||
);
|
);
|
||||||
if (this.args.onPlacesFound) {
|
this.router.transitionTo('place', clickedBookmark);
|
||||||
this.args.onPlacesFound([], clickedBookmark);
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -589,9 +597,7 @@ export default class MapComponent extends Component {
|
|||||||
// Normal behavior (sidebar is closed)
|
// Normal behavior (sidebar is closed)
|
||||||
if (clickedBookmark) {
|
if (clickedBookmark) {
|
||||||
console.log('Clicked bookmark:', clickedBookmark);
|
console.log('Clicked bookmark:', clickedBookmark);
|
||||||
if (this.args.onPlacesFound) {
|
this.router.transitionTo('place', clickedBookmark);
|
||||||
this.args.onPlacesFound([], clickedBookmark);
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -615,76 +621,21 @@ export default class MapComponent extends Component {
|
|||||||
this.searchOverlayElement.style.width = `${diameterInPixels}px`;
|
this.searchOverlayElement.style.width = `${diameterInPixels}px`;
|
||||||
this.searchOverlayElement.style.height = `${diameterInPixels}px`;
|
this.searchOverlayElement.style.height = `${diameterInPixels}px`;
|
||||||
this.searchOverlay.setPosition(event.coordinate);
|
this.searchOverlay.setPosition(event.coordinate);
|
||||||
this.searchOverlayElement.classList.add('active');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Fetch authoritative data via Overpass
|
// Start Search State
|
||||||
try {
|
this.mapUi.startSearch();
|
||||||
let pois = await this.osm.getNearbyPois(lat, lon, searchRadius);
|
|
||||||
|
|
||||||
// Sort by distance from click
|
// Transition to Search Route
|
||||||
pois = pois
|
const queryParams = {
|
||||||
.map((p) => {
|
lat: lat.toFixed(6),
|
||||||
// p is already normalized by service, so lat/lon are at top level
|
lon: lon.toFixed(6),
|
||||||
return {
|
};
|
||||||
...p,
|
if (selectedFeatureName) {
|
||||||
_distance: getDistance(lat, lon, p.lat, p.lon),
|
queryParams.q = selectedFeatureName;
|
||||||
};
|
|
||||||
})
|
|
||||||
.sort((a, b) => a._distance - b._distance);
|
|
||||||
|
|
||||||
let matchedPlace = null;
|
|
||||||
|
|
||||||
if (selectedFeatureName && pois.length > 0) {
|
|
||||||
// Heuristic:
|
|
||||||
// 1. Exact Name Match
|
|
||||||
matchedPlace = pois.find(
|
|
||||||
(p) =>
|
|
||||||
p.osmTags &&
|
|
||||||
(p.osmTags.name === selectedFeatureName ||
|
|
||||||
p.osmTags['name:en'] === selectedFeatureName)
|
|
||||||
);
|
|
||||||
|
|
||||||
// 2. If no exact match, look for VERY close (<=20m) and matching type
|
|
||||||
if (!matchedPlace) {
|
|
||||||
const topCandidate = pois[0];
|
|
||||||
if (topCandidate._distance <= 20) {
|
|
||||||
// Check type compatibility if available
|
|
||||||
// (visual tile 'class' is often 'cafe', osm tag is 'amenity'='cafe')
|
|
||||||
const pType =
|
|
||||||
topCandidate.osmTags.amenity ||
|
|
||||||
topCandidate.osmTags.shop ||
|
|
||||||
topCandidate.osmTags.tourism;
|
|
||||||
if (
|
|
||||||
selectedFeatureType &&
|
|
||||||
pType &&
|
|
||||||
(selectedFeatureType === pType ||
|
|
||||||
pType.includes(selectedFeatureType))
|
|
||||||
) {
|
|
||||||
console.log(
|
|
||||||
'Heuristic match found (distance + type):',
|
|
||||||
topCandidate
|
|
||||||
);
|
|
||||||
matchedPlace = topCandidate;
|
|
||||||
} else if (topCandidate._distance <= 10) {
|
|
||||||
// Even without type match, if it's super close (<=10m), it's likely the one.
|
|
||||||
console.log('Heuristic match found (proximity):', topCandidate);
|
|
||||||
matchedPlace = topCandidate;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.args.onPlacesFound) {
|
|
||||||
this.args.onPlacesFound(pois, matchedPlace);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to fetch POIs:', error);
|
|
||||||
} finally {
|
|
||||||
if (this.searchOverlayElement) {
|
|
||||||
this.searchOverlayElement.classList.remove('active');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.router.transitionTo('search', { queryParams });
|
||||||
};
|
};
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -693,6 +644,7 @@ export default class MapComponent extends Component {
|
|||||||
{{this.setupMap}}
|
{{this.setupMap}}
|
||||||
{{this.updateBookmarks}}
|
{{this.updateBookmarks}}
|
||||||
{{this.updateSelectedPin}}
|
{{this.updateSelectedPin}}
|
||||||
|
{{this.syncPulse}}
|
||||||
></div>
|
></div>
|
||||||
</template>
|
</template>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,13 +23,6 @@ export default class PlacesSidebar extends Component {
|
|||||||
if (this.args.onSelect) {
|
if (this.args.onSelect) {
|
||||||
this.args.onSelect(null);
|
this.args.onSelect(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback logic: if no list available, close sidebar
|
|
||||||
if (!this.args.places || this.args.places.length === 0) {
|
|
||||||
if (this.args.onClose) {
|
|
||||||
this.args.onClose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
|
|||||||
@@ -8,4 +8,5 @@ export default class Router extends EmberRouter {
|
|||||||
|
|
||||||
Router.map(function () {
|
Router.map(function () {
|
||||||
this.route('place', { path: '/place/:place_id' });
|
this.route('place', { path: '/place/:place_id' });
|
||||||
|
this.route('search');
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -49,6 +49,8 @@ export default class PlaceRoute extends Route {
|
|||||||
if (model) {
|
if (model) {
|
||||||
this.mapUi.selectPlace(model);
|
this.mapUi.selectPlace(model);
|
||||||
}
|
}
|
||||||
|
// Stop the pulse animation if it was running (e.g. redirected from search)
|
||||||
|
this.mapUi.stopSearch();
|
||||||
}
|
}
|
||||||
|
|
||||||
deactivate() {
|
deactivate() {
|
||||||
|
|||||||
@@ -0,0 +1,96 @@
|
|||||||
|
import Route from '@ember/routing/route';
|
||||||
|
import { service } from '@ember/service';
|
||||||
|
import { action } from '@ember/object';
|
||||||
|
import { getDistance } from '../utils/geo';
|
||||||
|
|
||||||
|
export default class SearchRoute extends Route {
|
||||||
|
@service osm;
|
||||||
|
@service mapUi;
|
||||||
|
@service storage;
|
||||||
|
@service router;
|
||||||
|
|
||||||
|
queryParams = {
|
||||||
|
lat: { refreshModel: true },
|
||||||
|
lon: { refreshModel: true },
|
||||||
|
q: { refreshModel: true },
|
||||||
|
};
|
||||||
|
|
||||||
|
async model(params) {
|
||||||
|
// If no coordinates, we can't search
|
||||||
|
if (!params.lat || !params.lon) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const lat = parseFloat(params.lat);
|
||||||
|
const lon = parseFloat(params.lon);
|
||||||
|
const searchRadius = params.q ? 30 : 50;
|
||||||
|
|
||||||
|
// Fetch POIs
|
||||||
|
let pois = await this.osm.getNearbyPois(lat, lon, searchRadius);
|
||||||
|
|
||||||
|
// Sort by distance from click
|
||||||
|
pois = pois
|
||||||
|
.map((p) => {
|
||||||
|
return {
|
||||||
|
...p,
|
||||||
|
_distance: getDistance(lat, lon, p.lat, p.lon),
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.sort((a, b) => a._distance - b._distance);
|
||||||
|
|
||||||
|
// Check if any of these are already bookmarked
|
||||||
|
// We resolve them to the bookmark version if they exist
|
||||||
|
pois = pois.map((p) => {
|
||||||
|
const saved = this.storage.findPlaceById(p.osmId);
|
||||||
|
return saved || p;
|
||||||
|
});
|
||||||
|
|
||||||
|
return pois;
|
||||||
|
}
|
||||||
|
|
||||||
|
afterModel(model, transition) {
|
||||||
|
const { q } = transition.to.queryParams;
|
||||||
|
|
||||||
|
// Heuristic Match Logic (ported from MapComponent)
|
||||||
|
if (q && model.length > 0) {
|
||||||
|
let matchedPlace = null;
|
||||||
|
|
||||||
|
// 1. Exact Name Match
|
||||||
|
matchedPlace = model.find(
|
||||||
|
(p) => p.osmTags && (p.osmTags.name === q || p.osmTags['name:en'] === q)
|
||||||
|
);
|
||||||
|
|
||||||
|
// 2. High Proximity Match (<= 10m)
|
||||||
|
// Note: MapComponent had logic for <=20m + type match.
|
||||||
|
// We might want to pass the 'type' in queryParams if we want to be that precise.
|
||||||
|
// For now, let's stick to name or very close proximity.
|
||||||
|
if (!matchedPlace) {
|
||||||
|
const topCandidate = model[0];
|
||||||
|
if (topCandidate._distance <= 10) {
|
||||||
|
matchedPlace = topCandidate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matchedPlace) {
|
||||||
|
// Direct transition!
|
||||||
|
this.router.replaceWith('place', matchedPlace);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop the pulse animation since search is done (and we are staying here)
|
||||||
|
this.mapUi.stopSearch();
|
||||||
|
}
|
||||||
|
|
||||||
|
setupController(controller, model) {
|
||||||
|
super.setupController(controller, model);
|
||||||
|
// Ensure pulse is stopped if we reach here
|
||||||
|
this.mapUi.stopSearch();
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
error() {
|
||||||
|
this.mapUi.stopSearch();
|
||||||
|
return true; // Bubble error
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ import { tracked } from '@glimmer/tracking';
|
|||||||
|
|
||||||
export default class MapUiService extends Service {
|
export default class MapUiService extends Service {
|
||||||
@tracked selectedPlace = null;
|
@tracked selectedPlace = null;
|
||||||
|
@tracked isSearching = false;
|
||||||
|
|
||||||
selectPlace(place) {
|
selectPlace(place) {
|
||||||
this.selectedPlace = place;
|
this.selectedPlace = place;
|
||||||
@@ -11,4 +12,12 @@ export default class MapUiService extends Service {
|
|||||||
clearSelection() {
|
clearSelection() {
|
||||||
this.selectedPlace = null;
|
this.selectedPlace = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
startSearch() {
|
||||||
|
this.isSearching = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
stopSearch() {
|
||||||
|
this.isSearching = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +1,28 @@
|
|||||||
import Component from '@glimmer/component';
|
import Component from '@glimmer/component';
|
||||||
import { pageTitle } from 'ember-page-title';
|
import { pageTitle } from 'ember-page-title';
|
||||||
import Map from '#components/map';
|
import Map from '#components/map';
|
||||||
import PlacesSidebar from '#components/places-sidebar';
|
|
||||||
import AppHeader from '#components/app-header';
|
import AppHeader from '#components/app-header';
|
||||||
import SettingsPane from '#components/settings-pane';
|
import SettingsPane from '#components/settings-pane';
|
||||||
import { service } from '@ember/service';
|
import { service } from '@ember/service';
|
||||||
import { tracked } from '@glimmer/tracking';
|
import { tracked } from '@glimmer/tracking';
|
||||||
import { action } from '@ember/object';
|
import { action } from '@ember/object';
|
||||||
import { eq } from 'ember-truth-helpers';
|
import { or } from 'ember-truth-helpers';
|
||||||
import { and, or } from 'ember-truth-helpers';
|
|
||||||
import { on } from '@ember/modifier';
|
import { on } from '@ember/modifier';
|
||||||
|
|
||||||
export default class ApplicationComponent extends Component {
|
export default class ApplicationComponent extends Component {
|
||||||
@service storage;
|
@service storage;
|
||||||
|
@service mapUi;
|
||||||
@service router;
|
@service router;
|
||||||
|
|
||||||
@tracked nearbyPlaces = null;
|
|
||||||
@tracked isSettingsOpen = false;
|
@tracked isSettingsOpen = false;
|
||||||
// @tracked bookmarksVersion = 0; // Moved to storage service
|
|
||||||
|
|
||||||
get isSidebarOpen() {
|
get isSidebarOpen() {
|
||||||
return !!this.nearbyPlaces || this.router.currentRouteName === 'place';
|
// We consider the sidebar "open" if we are in search or place routes.
|
||||||
|
// This helps the map know if it should shift the center or adjust view.
|
||||||
|
return (
|
||||||
|
this.router.currentRouteName === 'place' ||
|
||||||
|
this.router.currentRouteName === 'search'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -30,32 +32,6 @@ export default class ApplicationComponent extends Component {
|
|||||||
this.storage;
|
this.storage;
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
|
||||||
showPlaces(places, selectedPlace = null) {
|
|
||||||
// Helper to resolve a place to its bookmark if it exists
|
|
||||||
const resolvePlace = (p) => {
|
|
||||||
if (!p) return null;
|
|
||||||
// We use the OSM ID to check if we already have this place saved
|
|
||||||
const saved = this.storage.findPlaceById(p.osmId);
|
|
||||||
return saved || p;
|
|
||||||
};
|
|
||||||
|
|
||||||
const resolvedSelected = resolvePlace(selectedPlace);
|
|
||||||
const resolvedPlaces = places ? places.map(resolvePlace) : [];
|
|
||||||
|
|
||||||
// If we have a specific place, transition to the route
|
|
||||||
if (resolvedSelected) {
|
|
||||||
// Pass the FULL object model to avoid re-fetching!
|
|
||||||
// The Route's serialize() hook handles URL generation.
|
|
||||||
this.router.transitionTo('place', resolvedSelected);
|
|
||||||
this.nearbyPlaces = null; // Clear list when selecting specific
|
|
||||||
} else if (resolvedPlaces && resolvedPlaces.length > 0) {
|
|
||||||
// Show list case
|
|
||||||
this.nearbyPlaces = resolvedPlaces;
|
|
||||||
this.router.transitionTo('index');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
@action
|
||||||
toggleSettings() {
|
toggleSettings() {
|
||||||
this.isSettingsOpen = !this.isSettingsOpen;
|
this.isSettingsOpen = !this.isSettingsOpen;
|
||||||
@@ -66,29 +42,20 @@ export default class ApplicationComponent extends Component {
|
|||||||
this.isSettingsOpen = false;
|
this.isSettingsOpen = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
|
||||||
selectFromList(place) {
|
|
||||||
if (place) {
|
|
||||||
// Optimize: Pass full object to avoid fetch
|
|
||||||
this.router.transitionTo('place', place);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
@action
|
||||||
handleOutsideClick() {
|
handleOutsideClick() {
|
||||||
if (this.isSettingsOpen) {
|
if (this.isSettingsOpen) {
|
||||||
this.closeSettings();
|
this.closeSettings();
|
||||||
} else {
|
} else if (this.router.currentRouteName === 'search') {
|
||||||
this.closeSidebar();
|
this.router.transitionTo('index');
|
||||||
|
} else if (this.router.currentRouteName === 'place') {
|
||||||
|
// If in place route, decide if we want to go back to search or index
|
||||||
|
// For now, let's go to index or maybe back to search if search params exist?
|
||||||
|
// Simplest behavior: clear selection
|
||||||
|
this.router.transitionTo('index');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
|
||||||
closeSidebar() {
|
|
||||||
this.nearbyPlaces = null;
|
|
||||||
this.router.transitionTo('index');
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
@action
|
||||||
refreshBookmarks() {
|
refreshBookmarks() {
|
||||||
this.storage.notifyChange();
|
this.storage.notifyChange();
|
||||||
@@ -113,19 +80,10 @@ export default class ApplicationComponent extends Component {
|
|||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
<Map
|
<Map
|
||||||
@onPlacesFound={{this.showPlaces}}
|
|
||||||
@isSidebarOpen={{or this.isSidebarOpen this.isSettingsOpen}}
|
@isSidebarOpen={{or this.isSidebarOpen this.isSettingsOpen}}
|
||||||
@onOutsideClick={{this.handleOutsideClick}}
|
@onOutsideClick={{this.handleOutsideClick}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{{#if (and (eq this.router.currentRouteName "index") this.nearbyPlaces)}}
|
|
||||||
<PlacesSidebar
|
|
||||||
@places={{this.nearbyPlaces}}
|
|
||||||
@onSelect={{this.selectFromList}}
|
|
||||||
@onClose={{this.closeSidebar}}
|
|
||||||
/>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if this.isSettingsOpen}}
|
{{#if this.isSettingsOpen}}
|
||||||
<SettingsPane @onClose={{this.closeSettings}} />
|
<SettingsPane @onClose={{this.closeSettings}} />
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { tracked } from '@glimmer/tracking';
|
|||||||
export default class PlaceTemplate extends Component {
|
export default class PlaceTemplate extends Component {
|
||||||
@service router;
|
@service router;
|
||||||
@service storage;
|
@service storage;
|
||||||
|
@service mapUi;
|
||||||
|
|
||||||
@tracked localPlace = null;
|
@tracked localPlace = null;
|
||||||
|
|
||||||
@@ -72,8 +73,26 @@ export default class PlaceTemplate extends Component {
|
|||||||
this.storage.notifyChange();
|
this.storage.notifyChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
navigateBack(place) {
|
||||||
|
// The sidebar calls this with null when "Back" is clicked.
|
||||||
|
if (place === null) {
|
||||||
|
// If we have history, go back (preserves search state)
|
||||||
|
if (window.history.length > 1) {
|
||||||
|
window.history.back();
|
||||||
|
} else {
|
||||||
|
// Fallback if opened directly
|
||||||
|
this.router.transitionTo('index');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If a place is selected (unlikely in this view, but possible if we add related links)
|
||||||
|
this.router.transitionTo('place', place);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
close() {
|
close() {
|
||||||
|
// Clear search results so we don't fall back to the list
|
||||||
this.router.transitionTo('index');
|
this.router.transitionTo('index');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,6 +100,7 @@ export default class PlaceTemplate extends Component {
|
|||||||
<PlacesSidebar
|
<PlacesSidebar
|
||||||
@selectedPlace={{this.place}}
|
@selectedPlace={{this.place}}
|
||||||
@onClose={{this.close}}
|
@onClose={{this.close}}
|
||||||
|
@onSelect={{this.navigateBack}}
|
||||||
@onBookmarkChange={{this.refreshMap}}
|
@onBookmarkChange={{this.refreshMap}}
|
||||||
@onUpdate={{this.handleUpdate}}
|
@onUpdate={{this.handleUpdate}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import Component from '@glimmer/component';
|
||||||
|
import PlacesSidebar from '#components/places-sidebar';
|
||||||
|
import { service } from '@ember/service';
|
||||||
|
import { action } from '@ember/object';
|
||||||
|
|
||||||
|
export default class SearchTemplate extends Component {
|
||||||
|
@service router;
|
||||||
|
|
||||||
|
@action
|
||||||
|
selectPlace(place) {
|
||||||
|
if (place) {
|
||||||
|
this.router.transitionTo('place', place);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
close() {
|
||||||
|
this.router.transitionTo('index');
|
||||||
|
}
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<PlacesSidebar
|
||||||
|
@places={{@model}}
|
||||||
|
@onSelect={{this.selectPlace}}
|
||||||
|
@onClose={{this.close}}
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user