PageSourceSearch

https://mitiendapos.com.ar/assets/controllers/delivery_toggle_controller-c1aa4cad.js

js mitiendapos.com.ar collected 2026-09-24 14:34:29 UTC 4,541 bytes, 116 lines download raw bytes

1import { Controller } from "@hotwired/stimulus"
2import { numeroAR } from "lib/numero_ar"
3
4// Stimulus muy chico: muestra/oculta el bloque de direccion cuando el cliente
5// elige "Envio a domicilio" en el checkout del menu publico, y valida que
6// la coord seleccionada quede dentro del radio configurado por la tienda.
7export default class extends Controller {
8  static targets = ["addressBlock", "rangeWarning", "submitBtn", "cashBlock"]
9  static values = {
10    clubLat: Number,
11    clubLng: Number,
12    maxRadiusKm: Number
13  }
14
15  connect() {
16    // Cuando los hidden lat/lng del autocomplete cambian (por seleccion),
17    // recalculamos la distancia y mostramos warning si excede el radio.
18    const latInput = this.element.querySelector("input[name='delivery[latitude]']")
19    if (latInput) {
20      const observer = new MutationObserver(() => this._checkRadius())
21      observer.observe(latInput, { attributes: true, attributeFilter: ["value"] })
22      // Tambien por evento input por si JS setea via valueSetter
23      latInput.addEventListener("input", () => this._checkRadius())
24    }
25
26    // El "¿con cuánto pagás al repartidor?" solo aplica cuando los productos se
27    // pagan al recibir (modo reserva). En "pago online completo" ya pagó todo
28    // por MP, asi que lo ocultamos. Se re-evalua al cambiar el modo de pago.
29    this.element.closest("form")?.querySelectorAll("input[name=mode]").forEach((r) => {
30      r.addEventListener("change", () => this._syncCashBlock())
31    })
32    this._syncCashBlock()
33  }
34
35  _selectedMode() {
36    const form = this.element.closest("form")
37    if (!form) return null
38    const checked = form.querySelector("input[name=mode]:checked")
39    if (checked) return checked.value
40    const hidden = form.querySelector("input[name=mode]")
41    return hidden ? hidden.value : null
42  }
43
44  _syncCashBlock() {
45    if (!this.hasCashBlockTarget) return
46    const isReservation = this._selectedMode() === "reservation"
47    this.cashBlockTarget.classList.toggle("hidden", !isReservation)
48    if (!isReservation) {
49      const input = this.cashBlockTarget.querySelector("input")
50      if (input) input.value = "" // no mandar cash_offered si no aplica
51    }
52  }
53
54  show() {
55    if (this.hasAddressBlockTarget) {
56      this.addressBlockTarget.classList.remove("hidden")
57      const input = this.addressBlockTarget.querySelector("input[type=text]")
58      if (input) {
59        input.focus()
60        setTimeout(() => input.scrollIntoView({ behavior: "smooth", block: "start" }), 150)
61      }
62    }
63  }
64
65  hide() {
66    if (this.hasAddressBlockTarget) {
67      this.addressBlockTarget.classList.add("hidden")
68    }
69    this._hideWarning()
70  }
71
72  _checkRadius() {
73    const radius = this.maxRadiusKmValue
74    if (!radius || radius <= 0) return
75    const lat = numeroAR(this.element.querySelector("input[name='delivery[latitude]']")?.value)
76    const lng = numeroAR(this.element.querySelector("input[name='delivery[longitude]']")?.value)
77    if (isNaN(lat) || isNaN(lng)) { this._hideWarning(); return }
78    const clubLat = this.clubLatValue, clubLng = this.clubLngValue
79    if (!clubLat || !clubLng) return
80
81    const km = this._haversine(clubLat, clubLng, lat, lng)
82    if (km > radius) {
83      this._showWarning(`La dirección está a ${km.toFixed(1)} km de la tienda. El envío llega hasta ${radius} km.`)
84    } else {
85      this._hideWarning()
86    }
87  }
88
89  _showWarning(msg) {
90    if (!this.hasRangeWarningTarget) return
91    this.rangeWarningTarget.textContent = "⚠ " + msg
92    this.rangeWarningTarget.classList.remove("hidden")
93    // Form-level: deshabilita el submit hasta que cambie la direccion.
94    const form = this.element.closest("form")
95    const submit = form?.querySelector("input[type=submit]")
96    if (submit) { submit.disabled = true; submit.classList.add("opacity-50", "cursor-not-allowed") }
97  }
98
99  _hideWarning() {
100    if (this.hasRangeWarningTarget) this.rangeWarningTarget.classList.add("hidden")
101    const form = this.element.closest("form")
102    const submit = form?.querySelector("input[type=submit]")
103    if (submit) { submit.disabled = false; submit.classList.remove("opacity-50", "cursor-not-allowed") }
104  }
105
106  _haversine(lat1, lon1, lat2, lon2) {
107    const r = 6371
108    const toRad = d => d * Math.PI / 180
109    const dLat = toRad(lat2 - lat1)
110    const dLon = toRad(lon2 - lon1)
111    const a = Math.sin(dLat / 2) ** 2 +
112              Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) *
113              Math.sin(dLon / 2) ** 2
114    return r * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))
115  }
116}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.