changes
This commit is contained in:
150
fusion_clock/static/src/js/fusion_clock_location_places.js
Normal file
150
fusion_clock/static/src/js/fusion_clock_location_places.js
Normal file
@@ -0,0 +1,150 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { Component, onMounted, onWillUnmount, useRef, useState } from "@odoo/owl";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { standardFieldProps } from "@web/views/fields/standard_field_props";
|
||||
import { rpc } from "@web/core/network/rpc";
|
||||
|
||||
/**
|
||||
* Google Places Autocomplete widget for the address field.
|
||||
* Automatically geocodes the selected place and updates lat/lng on the record.
|
||||
*/
|
||||
export class FusionClockPlacesAutocomplete extends Component {
|
||||
static template = "fusion_clock.PlacesAutocomplete";
|
||||
static props = { ...standardFieldProps };
|
||||
|
||||
setup() {
|
||||
this.inputRef = useRef("addressInput");
|
||||
this.autocomplete = null;
|
||||
this._apiReady = false;
|
||||
|
||||
this.state = useState({
|
||||
value: this.props.record.data[this.props.name] || "",
|
||||
});
|
||||
|
||||
onMounted(() => this._init());
|
||||
onWillUnmount(() => this._cleanup());
|
||||
}
|
||||
|
||||
get isReadonly() {
|
||||
return this.props.readonly;
|
||||
}
|
||||
|
||||
async _getApiKey() {
|
||||
try {
|
||||
return await rpc("/web/dataset/call_kw", {
|
||||
model: "ir.config_parameter",
|
||||
method: "get_param",
|
||||
args: ["fusion_clock.google_maps_api_key", ""],
|
||||
kwargs: {},
|
||||
}) || "";
|
||||
} catch (e) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
async _waitForGoogleMaps() {
|
||||
if (window.google && window.google.maps && window.google.maps.places) {
|
||||
return true;
|
||||
}
|
||||
return new Promise((resolve) => {
|
||||
let attempts = 0;
|
||||
const check = setInterval(() => {
|
||||
attempts++;
|
||||
if (window.google && window.google.maps && window.google.maps.places) {
|
||||
clearInterval(check);
|
||||
resolve(true);
|
||||
}
|
||||
if (attempts > 50) {
|
||||
clearInterval(check);
|
||||
resolve(false);
|
||||
}
|
||||
}, 100);
|
||||
});
|
||||
}
|
||||
|
||||
async _loadGoogleMaps(apiKey) {
|
||||
if (window.google && window.google.maps) return;
|
||||
|
||||
if (document.querySelector('script[src*="maps.googleapis.com"]')) {
|
||||
await this._waitForGoogleMaps();
|
||||
return;
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const script = document.createElement("script");
|
||||
script.src = `https://maps.googleapis.com/maps/api/js?key=${apiKey}&libraries=places&callback=__fclkPlacesInit`;
|
||||
script.async = true;
|
||||
script.defer = true;
|
||||
window.__fclkPlacesInit = () => {
|
||||
delete window.__fclkPlacesInit;
|
||||
resolve();
|
||||
};
|
||||
script.onerror = () => reject(new Error("Failed to load Google Maps"));
|
||||
document.head.appendChild(script);
|
||||
});
|
||||
}
|
||||
|
||||
async _init() {
|
||||
if (this.isReadonly) return;
|
||||
|
||||
const apiKey = await this._getApiKey();
|
||||
if (!apiKey) return;
|
||||
|
||||
try {
|
||||
await this._loadGoogleMaps(apiKey);
|
||||
} catch (e) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this._waitForGoogleMaps();
|
||||
|
||||
if (!this.inputRef.el || !window.google || !window.google.maps.places) return;
|
||||
|
||||
this.autocomplete = new google.maps.places.Autocomplete(this.inputRef.el, {
|
||||
types: ["geocode", "establishment"],
|
||||
fields: ["formatted_address", "geometry", "name"],
|
||||
});
|
||||
|
||||
this.autocomplete.addListener("place_changed", () => {
|
||||
const place = this.autocomplete.getPlace();
|
||||
if (!place || !place.geometry) return;
|
||||
|
||||
const lat = place.geometry.location.lat();
|
||||
const lng = place.geometry.location.lng();
|
||||
const address = place.formatted_address || place.name || "";
|
||||
|
||||
this.state.value = address;
|
||||
this.props.record.update({
|
||||
[this.props.name]: address,
|
||||
latitude: Math.round(lat * 10000000) / 10000000,
|
||||
longitude: Math.round(lng * 10000000) / 10000000,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
onInput(ev) {
|
||||
this.state.value = ev.target.value;
|
||||
}
|
||||
|
||||
onChange(ev) {
|
||||
this.props.record.update({ [this.props.name]: ev.target.value });
|
||||
}
|
||||
|
||||
_cleanup() {
|
||||
if (this.autocomplete) {
|
||||
google.maps.event.clearInstanceListeners(this.autocomplete);
|
||||
this.autocomplete = null;
|
||||
}
|
||||
|
||||
const containers = document.querySelectorAll(".pac-container");
|
||||
containers.forEach((c) => c.remove());
|
||||
}
|
||||
}
|
||||
|
||||
FusionClockPlacesAutocomplete.template = "fusion_clock.PlacesAutocomplete";
|
||||
|
||||
registry.category("fields").add("fclk_places_autocomplete", {
|
||||
component: FusionClockPlacesAutocomplete,
|
||||
supportedTypes: ["char"],
|
||||
});
|
||||
Reference in New Issue
Block a user