Files
Odoo-Modules/fusion_clock/static/src/js/fusion_clock_location_places.js
gsinghpal b925766966 changes
2026-02-27 14:32:32 -05:00

151 lines
4.6 KiB
JavaScript

/** @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"],
});