520 lines
17 KiB
JavaScript
520 lines
17 KiB
JavaScript
|
export default class AddressFormHandler {
|
||
|
constructor(formSelectors, ajaxRoutes, livewireInstance, csrfToken) {
|
||
|
if (!ajaxRoutes && !ajaxRoutes.estado) {
|
||
|
throw new Error("ajax AddressFormHandler routes not found");
|
||
|
}
|
||
|
|
||
|
this.formSelectors = formSelectors;
|
||
|
this.ajaxRoutes = ajaxRoutes;
|
||
|
this.livewireInstance = livewireInstance; // Livewire Instance
|
||
|
this.csrfToken = csrfToken;
|
||
|
this.placeholders = {
|
||
|
pais: 'Selecciona el país',
|
||
|
estado: 'Selecciona el estado',
|
||
|
localidad: 'Selecciona la localidad',
|
||
|
municipio: 'Selecciona el municipio',
|
||
|
colonia: 'Selecciona la colonia',
|
||
|
codigo_postal: 'Ingresa el código postal',
|
||
|
}
|
||
|
|
||
|
this.synchronizedZipCode = false;
|
||
|
|
||
|
this.initializeSelects();
|
||
|
this.initializeZipCodeInput();
|
||
|
this.loadDefaultValuesFromLivewire();
|
||
|
}
|
||
|
|
||
|
|
||
|
initializeSelects() {
|
||
|
const { c_codigo_postal, c_pais, c_estado, c_localidad, c_municipio, c_colonia } = this.formSelectors;
|
||
|
|
||
|
// País
|
||
|
$(c_pais)
|
||
|
.select2({
|
||
|
language: "es",
|
||
|
placeholder: this.placeholders.pais,
|
||
|
allowClear: true,
|
||
|
width: "100%"
|
||
|
})
|
||
|
.on('select2:select', (e) => this.onCountrySelect(e))
|
||
|
.on('select2:clear', () => {
|
||
|
this.resetFieldWithDependencies('c_pais');
|
||
|
});
|
||
|
|
||
|
// Estado
|
||
|
$(c_estado)
|
||
|
.select2({
|
||
|
language: "es",
|
||
|
placeholder: this.placeholders.estado,
|
||
|
allowClear: true,
|
||
|
width: "100%"
|
||
|
})
|
||
|
.on('select2:select', (e) => this.onStateSelect(e))
|
||
|
.on('select2:clear', () => {
|
||
|
this.resetFieldWithDependencies('c_estado');
|
||
|
});
|
||
|
|
||
|
// Localidad
|
||
|
$(c_localidad)
|
||
|
.select2({
|
||
|
language: "es",
|
||
|
placeholder: this.placeholders.localidad,
|
||
|
allowClear: true,
|
||
|
width: "100%"
|
||
|
})
|
||
|
.on('select2:select', (e) => this.onLocalitySelect(e))
|
||
|
.on('select2:clear', () => {
|
||
|
this.resetFieldWithDependencies('c_localidad');
|
||
|
});
|
||
|
|
||
|
// Municipio
|
||
|
$(c_municipio)
|
||
|
.select2({
|
||
|
ajax: {
|
||
|
url: this.ajaxRoutes['municipio'],
|
||
|
type: "post",
|
||
|
delay: 250,
|
||
|
dataType: 'json',
|
||
|
data: (params) => ({
|
||
|
_token: this.csrfToken,
|
||
|
select2Mode: true,
|
||
|
searchTerm: params.term,
|
||
|
c_estado: $(c_estado).val(),
|
||
|
}),
|
||
|
processResults: (response) => ({
|
||
|
results: response
|
||
|
}),
|
||
|
cache: true
|
||
|
},
|
||
|
language: "es",
|
||
|
placeholder: this.placeholders.municipio,
|
||
|
allowClear: true,
|
||
|
width: "100%"
|
||
|
})
|
||
|
.on('select2:select', (e) => this.onMunicipalitySelect(e))
|
||
|
.on('select2:clear', () => {
|
||
|
this.resetFieldWithDependencies('c_municipio');
|
||
|
});
|
||
|
|
||
|
// Colonia
|
||
|
$(c_colonia)
|
||
|
.select2({
|
||
|
ajax: {
|
||
|
url: this.ajaxRoutes['colonia'],
|
||
|
type: "post",
|
||
|
delay: 250,
|
||
|
dataType: 'json',
|
||
|
data: (params) => ({
|
||
|
_token: this.csrfToken,
|
||
|
select2Mode: true,
|
||
|
searchTerm: params.term,
|
||
|
c_codigo_postal: $(c_codigo_postal).val(),
|
||
|
c_estado: $(c_estado).val(),
|
||
|
c_municipio: $(c_municipio).val(),
|
||
|
}),
|
||
|
processResults: (response) => ({
|
||
|
results: response
|
||
|
}),
|
||
|
cache: true
|
||
|
},
|
||
|
language: "es",
|
||
|
placeholder: this.placeholders.colonia,
|
||
|
allowClear: true,
|
||
|
width: "100%"
|
||
|
})
|
||
|
.on('select2:select', (e) => this.onColoniaSelect(e))
|
||
|
.on('select2:clear', () => {
|
||
|
this.livewireInstance.set('c_colonia', null, false);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
initializeZipCodeInput() {
|
||
|
const { c_codigo_postal } = this.formSelectors;
|
||
|
|
||
|
let lastPostalCode = ''; // Para evitar solicitudes duplicadas
|
||
|
let debounceTimeout;
|
||
|
|
||
|
$(c_codigo_postal).on('input', () => {
|
||
|
const postalCode = $(c_codigo_postal).val();
|
||
|
|
||
|
clearTimeout(debounceTimeout); // Cancelar el temporizador anterior
|
||
|
|
||
|
this.synchronizedZipCode = false;
|
||
|
|
||
|
debounceTimeout = setTimeout(() => {
|
||
|
if (postalCode.length === 5 && postalCode !== lastPostalCode) {
|
||
|
lastPostalCode = postalCode;
|
||
|
|
||
|
this.fetchZipCode(postalCode);
|
||
|
}
|
||
|
}, 300); // Ejecutar después de 300 ms de inactividad
|
||
|
});
|
||
|
}
|
||
|
|
||
|
async fetchZipCode(postalCode) {
|
||
|
try {
|
||
|
const response = await this.makeAjaxPost(this.ajaxRoutes.codigo_postal, {
|
||
|
_token: this.csrfToken,
|
||
|
searchTerm: postalCode,
|
||
|
firstRow: true,
|
||
|
rawMode: true
|
||
|
});
|
||
|
|
||
|
// Realizar solicitud AJAX para obtener datos del código postal
|
||
|
if (response.c_codigo_postal) {
|
||
|
// Limpiamos Interface
|
||
|
this.resetFieldWithDependencies('c_codigo_postal', response.c_codigo_postal);
|
||
|
|
||
|
// Cargamos Estados
|
||
|
this.toggleSelector('c_estado', response.c_estado);
|
||
|
|
||
|
// Cargar localidades
|
||
|
this.fillSelectOptions('c_localidad', {[response.c_localidad]: response.localidad }, response.c_localidad);
|
||
|
|
||
|
// Cargar municipios
|
||
|
this.fillSelectOptions('c_municipio', {[response.c_municipio]: response.municipio}, response.c_municipio);
|
||
|
|
||
|
// Abrir select de colonia
|
||
|
this.openSelect2('c_colonia');
|
||
|
|
||
|
// Marcar como sincronizado el código postal
|
||
|
this.synchronizedZipCode = true;
|
||
|
|
||
|
} else {
|
||
|
const { notification } = this.formSelectors;
|
||
|
|
||
|
// Emitir una notificación simple desde JavaScript
|
||
|
window.livewireNotification.emitNotification({
|
||
|
message: `Código postal ${postalCode} no encontrado.`,
|
||
|
type: 'warning',
|
||
|
target: notification,
|
||
|
});
|
||
|
}
|
||
|
|
||
|
} catch (error) {
|
||
|
console.error("Error al obtener datos del código postal:", error);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
onCountrySelect(event) {
|
||
|
const countryCode = event.params.data.id;
|
||
|
|
||
|
// Limpiamos Interface
|
||
|
this.resetFieldWithDependencies('c_pais', countryCode);
|
||
|
|
||
|
// Cargamos Interface de estados
|
||
|
this.fetchStates(countryCode);
|
||
|
}
|
||
|
|
||
|
async fetchStates(countryCode) {
|
||
|
try {
|
||
|
const response = await this.makeAjaxPost(this.ajaxRoutes.estado, {
|
||
|
_token: this.csrfToken,
|
||
|
c_pais: countryCode
|
||
|
});
|
||
|
|
||
|
// Realizamos solicitud AJAX para obtener datos de los estados
|
||
|
if (response) {
|
||
|
const { c_codigo_postal } = this.formSelectors;
|
||
|
|
||
|
// Cargar los estados
|
||
|
this.fillSelectOptions('c_estado', response);
|
||
|
|
||
|
// Ocultar y resetear los campos si no es México
|
||
|
if (countryCode === 'MEX') {
|
||
|
$('.if_local_address_show').show();
|
||
|
|
||
|
// Colocar el foco en el código postal
|
||
|
$(c_codigo_postal).focus();
|
||
|
|
||
|
} else {
|
||
|
$('.if_local_address_show').hide();
|
||
|
|
||
|
// Abrir select de estado
|
||
|
this.openSelect2('c_estado');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} catch (error) {
|
||
|
console.error("Error al cargar los estados:", error);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
onStateSelect(event) {
|
||
|
const { c_pais, direccion } = this.formSelectors;
|
||
|
const stateId = event.params.data.id;
|
||
|
|
||
|
// Limpiar Interface
|
||
|
this.resetFieldWithDependencies('c_estado', stateId);
|
||
|
|
||
|
// Si es México
|
||
|
if($(c_pais).val() == 'MEX'){
|
||
|
// Cargar localidades
|
||
|
this.fetchLocalities(stateId);
|
||
|
|
||
|
}else{
|
||
|
// Colocar el foco en la dirección
|
||
|
$(direccion).focus();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
async fetchLocalities(stateId) {
|
||
|
try {
|
||
|
const response = await this.makeAjaxPost(this.ajaxRoutes.localidad, {
|
||
|
_token: this.csrfToken,
|
||
|
c_estado: stateId,
|
||
|
limit: null,
|
||
|
});
|
||
|
|
||
|
if (response) {
|
||
|
const { c_localidad } = this.formSelectors;
|
||
|
|
||
|
// Cargar localidades
|
||
|
this.fillSelectOptions('c_localidad', response, c_localidad);
|
||
|
|
||
|
// Abrir select de localidad
|
||
|
this.openSelect2('c_localidad');
|
||
|
}
|
||
|
|
||
|
} catch (error) {
|
||
|
console.error("Error al cargar las localidades:", error);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
onLocalitySelect(event) {
|
||
|
const locationId = event.params.data.id;
|
||
|
|
||
|
// Limpiar Interface
|
||
|
this.resetFieldWithDependencies('c_localidad', locationId);
|
||
|
|
||
|
// Abrir select de municipio
|
||
|
this.openSelect2('c_municipio');
|
||
|
}
|
||
|
|
||
|
|
||
|
onMunicipalitySelect(event) {
|
||
|
const municipalityId = event.params.data.id;
|
||
|
|
||
|
// Limpiar Interface
|
||
|
this.resetFieldWithDependencies('c_municipio', municipalityId);
|
||
|
|
||
|
// Habilitamos colonias
|
||
|
this.toggleSelector('c_colonia');
|
||
|
|
||
|
// Abrir select colonia
|
||
|
this.openSelect2('c_colonia');
|
||
|
}
|
||
|
|
||
|
|
||
|
onColoniaSelect(event) {
|
||
|
const coloniaId = event.params.data.id;
|
||
|
|
||
|
// Cargar colonia
|
||
|
this.fillSelectOptions('c_colonia', {[coloniaId]: event.params.data.text}, coloniaId);
|
||
|
|
||
|
// Actualizar código postal si no está sincronizado
|
||
|
if(!this.synchronizedZipCode){
|
||
|
this.fetchZipCodeByData(this.livewireInstance.c_estado, this.livewireInstance.c_municipio, coloniaId);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
async fetchZipCodeByData(stateId, municipalityId, coloniaId) {
|
||
|
try {
|
||
|
const response = await this.makeAjaxPost(this.ajaxRoutes.colonia, {
|
||
|
_token: this.csrfToken,
|
||
|
c_estado: stateId,
|
||
|
c_municipio: municipalityId,
|
||
|
c_colonia: coloniaId,
|
||
|
firstRow: true,
|
||
|
rawMode: true,
|
||
|
});
|
||
|
|
||
|
if (response) {
|
||
|
const { c_codigo_postal, direccion } = this.formSelectors;
|
||
|
|
||
|
// Actualizar código postal si no está sincronizado
|
||
|
if($(c_codigo_postal).val() !== response.c_codigo_postal){
|
||
|
$(c_codigo_postal).val(response.c_codigo_postal);
|
||
|
|
||
|
// Actualizar en Livewire
|
||
|
this.livewireInstance.set('c_codigo_postal', response.c_codigo_postal, false);
|
||
|
}
|
||
|
|
||
|
// Marcar como sincronizado el código postal
|
||
|
this.synchronizedZipCode = true;
|
||
|
|
||
|
// Abrir select colonia
|
||
|
$(direccion).focus();
|
||
|
}
|
||
|
|
||
|
} catch (error) {
|
||
|
console.error("Error al cargar las localidades:", error);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
fillSelectOptions(selector, data, selected) {
|
||
|
const placeholder = this.placeholders[selector] || 'Selecciona una opción';
|
||
|
const $selector = $(this.formSelectors[selector]);
|
||
|
|
||
|
// Actualizar las opciones directamente en Livewire
|
||
|
this.livewireInstance.set(`${selector}_options`, data, false);
|
||
|
|
||
|
// Limpiar y agregar las nuevas opciones al selector
|
||
|
$selector.empty().append(new Option(placeholder, '', true, true)); // Agregar el placeholder
|
||
|
|
||
|
// Agregar opciones obtenidas por AJAX
|
||
|
if (typeof data === 'object' && Object.keys(data).length > 0) {
|
||
|
$.each(data, (id, value) => {
|
||
|
$selector.append(new Option(value, id, false, id == selected));
|
||
|
});
|
||
|
}
|
||
|
|
||
|
// Actualizar el valor seleccionado si corresponde
|
||
|
if (selected) {
|
||
|
this.livewireInstance.set(selector, selected, false);
|
||
|
|
||
|
$selector.val(selected).trigger('change.select2');
|
||
|
}
|
||
|
|
||
|
// Habilitar el select
|
||
|
$selector.prop('disabled', false).trigger('change.select2');
|
||
|
}
|
||
|
|
||
|
toggleSelector(selector, selected = null) {
|
||
|
const $selector = $(this.formSelectors[selector]);
|
||
|
|
||
|
if(typeof selected === 'number' || typeof selected === 'string'){
|
||
|
this.livewireInstance.set(selector, selected, false);
|
||
|
|
||
|
$selector
|
||
|
.val(selected)
|
||
|
.trigger('change.select2');
|
||
|
}
|
||
|
|
||
|
$selector.prop('disabled', !(selected || $selector.val())).trigger('change.select2');
|
||
|
}
|
||
|
|
||
|
resetFieldWithDependencies(field, value = null) {
|
||
|
const dependencies = {
|
||
|
c_codigo_postal: ['c_localidad', 'c_municipio', 'c_colonia'],
|
||
|
c_pais: ['c_codigo_postal', 'c_estado', 'c_localidad', 'c_municipio', 'c_colonia'],
|
||
|
c_estado: ['c_codigo_postal', 'c_localidad', 'c_municipio', 'c_colonia'],
|
||
|
c_localidad: ['c_codigo_postal', 'c_municipio', 'c_colonia'],
|
||
|
c_municipio: ['c_codigo_postal', 'c_colonia']
|
||
|
};
|
||
|
|
||
|
const resetFields = (fields) => {
|
||
|
fields.forEach((key) => {
|
||
|
const field = this.formSelectors[key]; // Obtener el selector por clave
|
||
|
const placeholder = this.placeholders[key] || 'Selecciona una opción';
|
||
|
const $field = $(field);
|
||
|
|
||
|
// Limpiar valor en Livewire
|
||
|
if (this.livewireInstance[key] !== null) {
|
||
|
this.livewireInstance.set(key, null, false);
|
||
|
}
|
||
|
|
||
|
if ($field.is('select')) {
|
||
|
// Resetear select
|
||
|
$field.empty()
|
||
|
.append(new Option(placeholder, '', true, true))
|
||
|
.prop('disabled', true)
|
||
|
.trigger('change.select2'); // Actualizar select2
|
||
|
|
||
|
} else if ($field.is('input[type="text"]') || $field.is('input[type="number"]')) {
|
||
|
// Resetear input de texto o número
|
||
|
$field.val('');
|
||
|
|
||
|
} else {
|
||
|
console.warn(`El campo ${field} no es un input ni un select válido.`);
|
||
|
}
|
||
|
});
|
||
|
};
|
||
|
|
||
|
// Limpieza de campos dependientes
|
||
|
if (dependencies[field]) {
|
||
|
resetFields(dependencies[field]);
|
||
|
}
|
||
|
|
||
|
// Actualizar el valor del campo principal en Livewire
|
||
|
this.livewireInstance.set(field, value, false);
|
||
|
}
|
||
|
|
||
|
openSelect2(selector) {
|
||
|
const $selector = $(this.formSelectors[selector]);
|
||
|
|
||
|
$selector.prop('disabled', false).trigger('change.select2');
|
||
|
|
||
|
setTimeout(() => $selector.select2('open'), 100);
|
||
|
}
|
||
|
|
||
|
async makeAjaxPost(url, data) {
|
||
|
try {
|
||
|
const response = await $.post(url, {
|
||
|
...data,
|
||
|
_token: this.csrfToken
|
||
|
});
|
||
|
|
||
|
return response;
|
||
|
|
||
|
} catch (error) {
|
||
|
console.error(`Error al realizar la solicitud AJAX a ${url}:`, error);
|
||
|
|
||
|
window.livewireNotification.emitNotification({
|
||
|
message: `Error al intentar realizar la solicitud.`,
|
||
|
target: this.formSelectors.notification,
|
||
|
type: 'danger',
|
||
|
});
|
||
|
|
||
|
return null; // Devuelve null en caso de error para evitar llamadas infinitas
|
||
|
}
|
||
|
}
|
||
|
|
||
|
loadDefaultValuesFromLivewire() {
|
||
|
const { c_pais, c_estado, c_localidad, c_municipio, c_colonia, c_codigo_postal } = this.livewireInstance;
|
||
|
const { c_pais: paisSelector, c_estado: estadoSelector, c_localidad: localidadSelector, c_municipio: municipioSelector, c_colonia: coloniaSelector, c_codigo_postal: codigoPostalSelector } = this.formSelectors;
|
||
|
|
||
|
// Cargar país por defecto
|
||
|
if (c_pais && $(paisSelector).val() !== c_pais) {
|
||
|
$(paisSelector).val(c_pais).trigger('change.select2');
|
||
|
}
|
||
|
|
||
|
// Cargar estados por defecto si están disponibles
|
||
|
if (c_estado && $(estadoSelector).val() !== c_estado) {
|
||
|
$(estadoSelector).val(c_estado).trigger('change.select2');
|
||
|
}
|
||
|
|
||
|
// Si el país es México, mostrar campos de dirección
|
||
|
if (c_pais === 'MEX') {
|
||
|
$('.if_local_address_show').show();
|
||
|
|
||
|
if (c_localidad && $(localidadSelector).val() !== c_localidad) {
|
||
|
$(localidadSelector).val(c_localidad).trigger('change.select2');
|
||
|
}
|
||
|
|
||
|
if (c_municipio && $(municipioSelector).val() !== c_municipio) {
|
||
|
$(municipioSelector).val(c_municipio).trigger('change.select2');
|
||
|
}
|
||
|
|
||
|
if (c_colonia && $(coloniaSelector).val() !== c_colonia) {
|
||
|
$(coloniaSelector).val(c_colonia).trigger('change.select2');
|
||
|
}
|
||
|
|
||
|
if (c_codigo_postal && $(codigoPostalSelector).val() !== c_codigo_postal) {
|
||
|
$(codigoPostalSelector).val(c_codigo_postal);
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
$('.if_local_address_show').hide();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
window.AddressFormHandler = AddressFormHandler;
|