@quantaroute/checkout 1.2.0 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -451,6 +451,14 @@ const MapPinSelector = ({
451
451
  ] });
452
452
  };
453
453
  const DEFAULT_BASE_URL = "https://api.quantaroute.com";
454
+ function makeTimeoutSignal(ms) {
455
+ if (typeof AbortSignal !== "undefined" && typeof AbortSignal.timeout === "function") {
456
+ return AbortSignal.timeout(ms);
457
+ }
458
+ const ctrl = new AbortController();
459
+ setTimeout(() => ctrl.abort(), ms);
460
+ return ctrl.signal;
461
+ }
454
462
  async function lookupLocation(lat, lng, apiKey, baseUrl = DEFAULT_BASE_URL) {
455
463
  const url = `${baseUrl.replace(/\/$/, "")}/v1/location/lookup`;
456
464
  let res;
@@ -462,11 +470,10 @@ async function lookupLocation(lat, lng, apiKey, baseUrl = DEFAULT_BASE_URL) {
462
470
  "x-api-key": apiKey
463
471
  },
464
472
  body: JSON.stringify({ latitude: lat, longitude: lng }),
465
- signal: AbortSignal.timeout(15e3)
466
- // 15s timeout
473
+ signal: makeTimeoutSignal(15e3)
467
474
  });
468
475
  } catch (err) {
469
- if (err instanceof Error && err.name === "TimeoutError") {
476
+ if (err instanceof Error && (err.name === "TimeoutError" || err.name === "AbortError")) {
470
477
  throw new Error("Request timed out. Check your internet connection.");
471
478
  }
472
479
  throw new Error(`Network error: ${err instanceof Error ? err.message : String(err)}`);
@@ -502,11 +509,10 @@ async function reverseGeocode(digipin, apiKey, baseUrl = DEFAULT_BASE_URL) {
502
509
  "x-api-key": apiKey
503
510
  },
504
511
  body: JSON.stringify({ digipin }),
505
- signal: AbortSignal.timeout(15e3)
506
- // 15s timeout
512
+ signal: makeTimeoutSignal(15e3)
507
513
  });
508
514
  } catch (err) {
509
- if (err instanceof Error && err.name === "TimeoutError") {
515
+ if (err instanceof Error && (err.name === "TimeoutError" || err.name === "AbortError")) {
510
516
  throw new Error("Request timed out. Check your internet connection.");
511
517
  }
512
518
  throw new Error(`Network error: ${err instanceof Error ? err.message : String(err)}`);
@@ -1,4 +1,4 @@
1
- !function(e,r){"object"==typeof exports&&"undefined"!=typeof module?r(exports,require("react/jsx-runtime"),require("react"),require("maplibre-gl")):"function"==typeof define&&define.amd?define(["exports","react/jsx-runtime","react","maplibre-gl"],r):r((e="undefined"!=typeof globalThis?globalThis:e||self).QuantaRouteCheckout={},e.ReactJSXRuntime,e.React,e.maplibregl)}(this,function(e,r,a,n){"use strict"
1
+ !function(e,r){"object"==typeof exports&&"undefined"!=typeof module?r(exports,require("react/jsx-runtime"),require("react"),require("maplibre-gl")):"function"==typeof define&&define.amd?define(["exports","react/jsx-runtime","react","maplibre-gl"],r):r((e="undefined"!=typeof globalThis?globalThis:e||self).QuantaRouteCheckout={},e.ReactJSXRuntime,e.React,e.maplibregl)}(this,function(e,r,n,t){"use strict"
2
2
 
3
3
  ;/*!
4
4
  * @quantaroute/checkout v1.2.0
@@ -19,76 +19,78 @@
19
19
  * expo >= 49.0.0
20
20
  * expo-osm-sdk >= 2.0.0
21
21
  * expo-location >= 17.0.0
22
- */const t=[["F","C","9","8"],["J","3","2","7"],["K","4","5","6"],["L","M","P","T"]],i={minLat:2.5,maxLat:38.5,minLon:63.5,maxLon:99.5}
22
+ */const a=[["F","C","9","8"],["J","3","2","7"],["K","4","5","6"],["L","M","P","T"]],i={minLat:2.5,maxLat:38.5,minLon:63.5,maxLon:99.5}
23
23
  function o(e,r){if(e<i.minLat||e>i.maxLat)throw new Error(`Latitude ${e} out of range. Must be between ${i.minLat} and ${i.maxLat}`)
24
24
  if(r<i.minLon||r>i.maxLon)throw new Error(`Longitude ${r} out of range. Must be between ${i.minLon} and ${i.maxLon}`)
25
- let a=i.minLat,n=i.maxLat,o=i.minLon,l=i.maxLon,s=""
26
- for(let i=1;i<=10;i++){const d=(n-a)/4,c=(l-o)/4
27
- let u=3-Math.floor((e-a)/d),h=Math.floor((r-o)/c)
28
- u=Math.max(0,Math.min(u,3)),h=Math.max(0,Math.min(h,3)),s+=t[u][h],3!==i&&6!==i||(s+="-"),n=a+d*(4-u),a+=d*(3-u),o+=c*h,l=o+c}return s}function l(e){const r=e.replace(/-/g,"")
25
+ let n=i.minLat,t=i.maxLat,o=i.minLon,l=i.maxLon,s=""
26
+ for(let i=1;i<=10;i++){const d=(t-n)/4,c=(l-o)/4
27
+ let u=3-Math.floor((e-n)/d),h=Math.floor((r-o)/c)
28
+ u=Math.max(0,Math.min(u,3)),h=Math.max(0,Math.min(h,3)),s+=a[u][h],3!==i&&6!==i||(s+="-"),t=n+d*(4-u),n+=d*(3-u),o+=c*h,l=o+c}return s}function l(e){const r=e.replace(/-/g,"")
29
29
  if(10!==r.length)throw new Error("Invalid DIGIPIN: must be 10 characters (excluding dashes)")
30
- let a=i.minLat,n=i.maxLat,o=i.minLon,l=i.maxLon
30
+ let n=i.minLat,t=i.maxLat,o=i.minLon,l=i.maxLon
31
31
  for(let i=0;i<10;i++){const e=r[i]
32
32
  let s=!1,d=-1,c=-1
33
- for(let r=0;r<4;r++){for(let a=0;a<4;a++)if(t[r][a]===e){d=r,c=a,s=!0
33
+ for(let r=0;r<4;r++){for(let n=0;n<4;n++)if(a[r][n]===e){d=r,c=n,s=!0
34
34
  break}if(s)break}if(!s)throw new Error(`Invalid character in DIGIPIN: '${e}'`)
35
- const u=(n-a)/4,h=(l-o)/4,p=o+h*(c+1)
36
- a=n-u*(d+1),n-=u*d,o+=h*c,l=p}return{latitude:((a+n)/2).toFixed(6),longitude:((o+l)/2).toFixed(6)}}function s(e,r){return e>=i.minLat&&e<=i.maxLat&&r>=i.minLon&&r<=i.maxLon}function d(){const[e,r]=a.useState({loading:!1,error:null})
37
- return{...e,locate:a.useCallback(e=>{navigator.geolocation?(r({loading:!0,error:null}),navigator.geolocation.getCurrentPosition(({coords:a})=>{r({loading:!1,error:null}),e(a.latitude,a.longitude)},e=>{let a="Unable to get location."
38
- e.code===e.PERMISSION_DENIED?a="Location permission denied. Please enable it in browser settings.":e.code===e.POSITION_UNAVAILABLE?a="Location unavailable. Try again or place the pin manually.":e.code===e.TIMEOUT&&(a="Location request timed out. Try again."),r({loading:!1,error:a})},{enableHighAccuracy:!0,timeout:12e3,maximumAge:0})):r({loading:!1,error:"Geolocation is not supported by your browser."})},[]),clearError:a.useCallback(()=>{r(e=>({...e,error:null}))},[])}}function c(e,r){if(!s(e,r))return null
39
- try{return o(e,r)}catch{return null}}const u=({onLocationConfirm:e,defaultLat:t,defaultLng:i,mapHeight:o="380px",theme:l="light",indiaBoundaryUrl:s})=>{const u=a.useRef(null),h=a.useRef(null),p=a.useRef(null),m=t??13.00427,g=i??77.589291,f=void 0!==t&&void 0!==i,[q,b]=a.useState(m),[y,N]=a.useState(g),[v,_]=a.useState(()=>c(m,g)),[w,k]=a.useState(!1),{loading:x,error:L,locate:C,clearError:E}=d(),S=a.useCallback((e,r)=>{_(c(e,r))},[])
40
- a.useEffect(()=>{if(!u.current)return
41
- const e=s?fetch(s).then(e=>e.ok?e.json():null).catch(()=>null):Promise.resolve(null),r=f?18:9,a=new n.Map({container:u.current,style:"https://basemaps.cartocdn.com/gl/positron-gl-style/style.json",center:[g,m],zoom:r,minZoom:6,attributionControl:!1,touchZoomRotate:!0})
42
- return a.addControl(new n.AttributionControl({compact:!0,customAttribution:'© <a href="https://www.openstreetmap.org/copyright" target="_blank" rel="noopener">OpenStreetMap</a> contributors &nbsp;© <a href="https://carto.com/attributions" target="_blank" rel="noopener">CARTO</a>'}),"bottom-right"),a.addControl(new n.NavigationControl({showCompass:!1}),"top-right"),a.on("load",()=>{k(!0),e.then(e=>{if(e&&a.isStyleLoaded())try{a.addSource("india-boundary",{type:"geojson",data:e}),a.addLayer({id:"india-boundary-line",type:"line",source:"india-boundary",paint:{"line-color":"#94a3b8","line-width":1.5,"line-opacity":.65}})}catch{}})
35
+ const u=(t-n)/4,h=(l-o)/4,p=o+h*(c+1)
36
+ n=t-u*(d+1),t-=u*d,o+=h*c,l=p}return{latitude:((n+t)/2).toFixed(6),longitude:((o+l)/2).toFixed(6)}}function s(e,r){return e>=i.minLat&&e<=i.maxLat&&r>=i.minLon&&r<=i.maxLon}function d(){const[e,r]=n.useState({loading:!1,error:null})
37
+ return{...e,locate:n.useCallback(e=>{navigator.geolocation?(r({loading:!0,error:null}),navigator.geolocation.getCurrentPosition(({coords:n})=>{r({loading:!1,error:null}),e(n.latitude,n.longitude)},e=>{let n="Unable to get location."
38
+ e.code===e.PERMISSION_DENIED?n="Location permission denied. Please enable it in browser settings.":e.code===e.POSITION_UNAVAILABLE?n="Location unavailable. Try again or place the pin manually.":e.code===e.TIMEOUT&&(n="Location request timed out. Try again."),r({loading:!1,error:n})},{enableHighAccuracy:!0,timeout:12e3,maximumAge:0})):r({loading:!1,error:"Geolocation is not supported by your browser."})},[]),clearError:n.useCallback(()=>{r(e=>({...e,error:null}))},[])}}function c(e,r){if(!s(e,r))return null
39
+ try{return o(e,r)}catch{return null}}const u=({onLocationConfirm:e,defaultLat:a,defaultLng:i,mapHeight:o="380px",theme:l="light",indiaBoundaryUrl:s})=>{const u=n.useRef(null),h=n.useRef(null),p=n.useRef(null),m=a??13.00427,f=i??77.589291,g=void 0!==a&&void 0!==i,[b,q]=n.useState(m),[y,N]=n.useState(f),[v,_]=n.useState(()=>c(m,f)),[w,k]=n.useState(!1),{loading:x,error:L,locate:C,clearError:E}=d(),A=n.useCallback((e,r)=>{_(c(e,r))},[])
40
+ n.useEffect(()=>{if(!u.current)return
41
+ const e=s?fetch(s).then(e=>e.ok?e.json():null).catch(()=>null):Promise.resolve(null),r=g?18:9,n=new t.Map({container:u.current,style:"https://basemaps.cartocdn.com/gl/positron-gl-style/style.json",center:[f,m],zoom:r,minZoom:6,attributionControl:!1,touchZoomRotate:!0})
42
+ return n.addControl(new t.AttributionControl({compact:!0,customAttribution:'© <a href="https://www.openstreetmap.org/copyright" target="_blank" rel="noopener">OpenStreetMap</a> contributors &nbsp;© <a href="https://carto.com/attributions" target="_blank" rel="noopener">CARTO</a>'}),"bottom-right"),n.addControl(new t.NavigationControl({showCompass:!1}),"top-right"),n.on("load",()=>{k(!0),e.then(e=>{if(e&&n.isStyleLoaded())try{n.addSource("india-boundary",{type:"geojson",data:e}),n.addLayer({id:"india-boundary-line",type:"line",source:"india-boundary",paint:{"line-color":"#94a3b8","line-width":1.5,"line-opacity":.65}})}catch{}})
43
43
  const r=function(){const e=document.createElement("div")
44
- return e.className="qr-pin",e.setAttribute("aria-label","Drag to adjust your location"),e.style.width="40px",e.style.height="52px",e.innerHTML='\n <svg\n class="qr-pin__svg"\n width="40"\n height="52"\n viewBox="0 0 40 52"\n overflow="visible"\n fill="none"\n xmlns="http://www.w3.org/2000/svg"\n aria-hidden="true"\n >\n <defs>\n \x3c!--\n The filter region must be generous enough to contain the shadow blur.\n stdDeviation=3 → ~9px blur radius; our region extends 50% on each side.\n overflow="visible" on the <svg> lets it paint outside the viewport.\n --\x3e\n <filter id="qr-pin-shadow" x="-60%" y="-40%" width="220%" height="220%">\n <feDropShadow dx="0" dy="3" stdDeviation="3.5"\n flood-color="#000" flood-opacity="0.28"/>\n </filter>\n </defs>\n\n \x3c!-- Teardrop body — tip is at (20, 52) = bottom-center of the SVG --\x3e\n <path\n d="M20 0C9.402 0 0 9.402 0 20C0 34 20 52 20 52S40 34 40 20C40 9.402 30.598 0 20 0Z"\n fill="#0ea5e9"\n filter="url(#qr-pin-shadow)"\n />\n \x3c!-- Dot inside the pin --\x3e\n <circle cx="20" cy="20" r="9" fill="white"/>\n <circle cx="20" cy="20" r="5" fill="#0ea5e9"/>\n </svg>\n\n \x3c!--\n Pulse ring — position: absolute inside .qr-pin (position: relative).\n bottom: -(height/2) → bottom: -10px centres the 20×20 circle\n exactly at y = PIN_H = 52px = the pin tip coordinate.\n --\x3e\n <div class="qr-pin__pulse" aria-hidden="true"></div>\n ',e}(),t=new n.Marker({element:r,draggable:!0,anchor:"bottom"}).setLngLat([g,m]).addTo(a)
45
- t.on("drag",()=>{const e=t.getLngLat()
46
- b(e.lat),N(e.lng),S(e.lat,e.lng)}),t.on("dragend",()=>{const e=t.getLngLat()
47
- a.easeTo({center:[e.lng,e.lat],duration:250})}),p.current=t}),a.on("click",e=>{var r
48
- const n=e.lngLat
49
- null==(r=p.current)||r.setLngLat([n.lng,n.lat]),b(n.lat),N(n.lng),S(n.lat,n.lng),a.easeTo({center:[n.lng,n.lat],duration:200})}),h.current=a,()=>{a.remove(),h.current=null,p.current=null}},[])
50
- const A=a.useCallback(()=>{E(),C((e,r)=>{var a
51
- b(e),N(r),S(e,r),h.current&&h.current.flyTo({center:[r,e],zoom:18,duration:1400,essential:!0}),null==(a=p.current)||a.setLngLat([r,e])})},[C,E,S]),T=a.useCallback(()=>{v&&e(q,y,v)},[q,y,v,e]),I=null!==v,M=I&&w
52
- return r.jsxs("div",{className:"qr-map-wrapper",children:[r.jsxs("div",{className:"qr-step-header",children:[r.jsx("div",{className:"qr-step-badge",children:"1"}),r.jsxs("div",{className:"qr-step-text",children:[r.jsx("span",{className:"qr-step-title",children:"Pin Your Location"}),r.jsx("span",{className:"qr-step-sub",children:"Tap the map or drag the pin to your exact home / office"})]})]}),r.jsxs("div",{className:"qr-map-outer",style:{height:o},children:[r.jsx("div",{ref:u,className:"qr-map-canvas"}),v&&r.jsxs("div",{className:"qr-digipin-badge","aria-live":"polite","aria-label":`DigiPin: ${v}`,children:[r.jsx("span",{className:"qr-digipin-badge__label",children:"DigiPin"}),r.jsx("span",{className:"qr-digipin-badge__code",children:v})]}),!I&&w&&r.jsx("div",{className:"qr-map-notice",role:"status",children:"Move map to India to get a DigiPin"}),r.jsx("button",{className:"qr-locate-btn"+(x?" qr-locate-btn--loading":""),onClick:A,disabled:x,title:"Use my current location","aria-label":"Use my current location",type:"button",children:x?r.jsx("span",{className:"qr-spinner","aria-hidden":"true"}):r.jsxs("svg",{"aria-hidden":"true",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[r.jsx("circle",{cx:"12",cy:"12",r:"3",fill:"currentColor",stroke:"none"}),r.jsx("circle",{cx:"12",cy:"12",r:"7"}),r.jsx("path",{d:"M12 2v3M12 19v3M2 12h3M19 12h3"})]})}),L&&r.jsxs("div",{className:"qr-geo-error",role:"alert",children:[r.jsx("span",{children:L}),r.jsx("button",{type:"button",className:"qr-geo-error__dismiss",onClick:E,"aria-label":"Dismiss error",children:"×"})]})]}),w&&r.jsxs("div",{className:"qr-coords-strip","aria-label":"Current pin coordinates",children:[r.jsxs("span",{children:[q.toFixed(5),"° N"]}),r.jsx("span",{className:"qr-coords-sep",children:"·"}),r.jsxs("span",{children:[y.toFixed(5),"° E"]})]}),r.jsxs("div",{className:"qr-map-actions",children:[r.jsxs("button",{type:"button",className:"qr-btn qr-btn--primary qr-btn--full",onClick:T,disabled:!M,children:["Confirm Location",r.jsx("svg",{"aria-hidden":"true",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2.5",strokeLinecap:"round",strokeLinejoin:"round",children:r.jsx("path",{d:"M5 12h14M12 5l7 7-7 7"})})]}),!I&&w&&r.jsx("p",{className:"qr-map-hint",children:"Move the pin to a location in India to continue"})]})]})},h="https://api.quantaroute.com"
53
- async function p(e,r,a,n=h){const t=`${n.replace(/\/$/,"")}/v1/location/lookup`
44
+ return e.className="qr-pin",e.setAttribute("aria-label","Drag to adjust your location"),e.style.width="40px",e.style.height="52px",e.innerHTML='\n <svg\n class="qr-pin__svg"\n width="40"\n height="52"\n viewBox="0 0 40 52"\n overflow="visible"\n fill="none"\n xmlns="http://www.w3.org/2000/svg"\n aria-hidden="true"\n >\n <defs>\n \x3c!--\n The filter region must be generous enough to contain the shadow blur.\n stdDeviation=3 → ~9px blur radius; our region extends 50% on each side.\n overflow="visible" on the <svg> lets it paint outside the viewport.\n --\x3e\n <filter id="qr-pin-shadow" x="-60%" y="-40%" width="220%" height="220%">\n <feDropShadow dx="0" dy="3" stdDeviation="3.5"\n flood-color="#000" flood-opacity="0.28"/>\n </filter>\n </defs>\n\n \x3c!-- Teardrop body — tip is at (20, 52) = bottom-center of the SVG --\x3e\n <path\n d="M20 0C9.402 0 0 9.402 0 20C0 34 20 52 20 52S40 34 40 20C40 9.402 30.598 0 20 0Z"\n fill="#0ea5e9"\n filter="url(#qr-pin-shadow)"\n />\n \x3c!-- Dot inside the pin --\x3e\n <circle cx="20" cy="20" r="9" fill="white"/>\n <circle cx="20" cy="20" r="5" fill="#0ea5e9"/>\n </svg>\n\n \x3c!--\n Pulse ring — position: absolute inside .qr-pin (position: relative).\n bottom: -(height/2) → bottom: -10px centres the 20×20 circle\n exactly at y = PIN_H = 52px = the pin tip coordinate.\n --\x3e\n <div class="qr-pin__pulse" aria-hidden="true"></div>\n ',e}(),a=new t.Marker({element:r,draggable:!0,anchor:"bottom"}).setLngLat([f,m]).addTo(n)
45
+ a.on("drag",()=>{const e=a.getLngLat()
46
+ q(e.lat),N(e.lng),A(e.lat,e.lng)}),a.on("dragend",()=>{const e=a.getLngLat()
47
+ n.easeTo({center:[e.lng,e.lat],duration:250})}),p.current=a}),n.on("click",e=>{var r
48
+ const t=e.lngLat
49
+ null==(r=p.current)||r.setLngLat([t.lng,t.lat]),q(t.lat),N(t.lng),A(t.lat,t.lng),n.easeTo({center:[t.lng,t.lat],duration:200})}),h.current=n,()=>{n.remove(),h.current=null,p.current=null}},[])
50
+ const S=n.useCallback(()=>{E(),C((e,r)=>{var n
51
+ q(e),N(r),A(e,r),h.current&&h.current.flyTo({center:[r,e],zoom:18,duration:1400,essential:!0}),null==(n=p.current)||n.setLngLat([r,e])})},[C,E,A]),T=n.useCallback(()=>{v&&e(b,y,v)},[b,y,v,e]),I=null!==v,M=I&&w
52
+ return r.jsxs("div",{className:"qr-map-wrapper",children:[r.jsxs("div",{className:"qr-step-header",children:[r.jsx("div",{className:"qr-step-badge",children:"1"}),r.jsxs("div",{className:"qr-step-text",children:[r.jsx("span",{className:"qr-step-title",children:"Pin Your Location"}),r.jsx("span",{className:"qr-step-sub",children:"Tap the map or drag the pin to your exact home / office"})]})]}),r.jsxs("div",{className:"qr-map-outer",style:{height:o},children:[r.jsx("div",{ref:u,className:"qr-map-canvas"}),v&&r.jsxs("div",{className:"qr-digipin-badge","aria-live":"polite","aria-label":`DigiPin: ${v}`,children:[r.jsx("span",{className:"qr-digipin-badge__label",children:"DigiPin"}),r.jsx("span",{className:"qr-digipin-badge__code",children:v})]}),!I&&w&&r.jsx("div",{className:"qr-map-notice",role:"status",children:"Move map to India to get a DigiPin"}),r.jsx("button",{className:"qr-locate-btn"+(x?" qr-locate-btn--loading":""),onClick:S,disabled:x,title:"Use my current location","aria-label":"Use my current location",type:"button",children:x?r.jsx("span",{className:"qr-spinner","aria-hidden":"true"}):r.jsxs("svg",{"aria-hidden":"true",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[r.jsx("circle",{cx:"12",cy:"12",r:"3",fill:"currentColor",stroke:"none"}),r.jsx("circle",{cx:"12",cy:"12",r:"7"}),r.jsx("path",{d:"M12 2v3M12 19v3M2 12h3M19 12h3"})]})}),L&&r.jsxs("div",{className:"qr-geo-error",role:"alert",children:[r.jsx("span",{children:L}),r.jsx("button",{type:"button",className:"qr-geo-error__dismiss",onClick:E,"aria-label":"Dismiss error",children:"×"})]})]}),w&&r.jsxs("div",{className:"qr-coords-strip","aria-label":"Current pin coordinates",children:[r.jsxs("span",{children:[b.toFixed(5),"° N"]}),r.jsx("span",{className:"qr-coords-sep",children:"·"}),r.jsxs("span",{children:[y.toFixed(5),"° E"]})]}),r.jsxs("div",{className:"qr-map-actions",children:[r.jsxs("button",{type:"button",className:"qr-btn qr-btn--primary qr-btn--full",onClick:T,disabled:!M,children:["Confirm Location",r.jsx("svg",{"aria-hidden":"true",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2.5",strokeLinecap:"round",strokeLinejoin:"round",children:r.jsx("path",{d:"M5 12h14M12 5l7 7-7 7"})})]}),!I&&w&&r.jsx("p",{className:"qr-map-hint",children:"Move the pin to a location in India to continue"})]})]})},h="https://api.quantaroute.com"
53
+ function p(e){if("undefined"!=typeof AbortSignal&&"function"==typeof AbortSignal.timeout)return AbortSignal.timeout(e)
54
+ const r=new AbortController
55
+ return setTimeout(()=>r.abort(),e),r.signal}async function m(e,r,n,t=h){const a=`${t.replace(/\/$/,"")}/v1/location/lookup`
54
56
  let i
55
- try{i=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":a},body:JSON.stringify({latitude:e,longitude:r}),signal:AbortSignal.timeout(15e3)})}catch(l){if(l instanceof Error&&"TimeoutError"===l.name)throw new Error("Request timed out. Check your internet connection.")
57
+ try{i=await fetch(a,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":n},body:JSON.stringify({latitude:e,longitude:r}),signal:p(15e3)})}catch(l){if(l instanceof Error&&("TimeoutError"===l.name||"AbortError"===l.name))throw new Error("Request timed out. Check your internet connection.")
56
58
  throw new Error(`Network error: ${l instanceof Error?l.message:String(l)}`)}if(!i.ok){let e=""
57
59
  try{e=await i.text()}catch{}if(401===i.status||403===i.status)throw new Error("Invalid API key. Check your QuantaRoute API key.")
58
60
  if(429===i.status)throw new Error("Rate limit exceeded. Upgrade your plan or try again later.")
59
61
  throw new Error(`API error ${i.status}: ${e||i.statusText}`)}const o=await i.json()
60
62
  if(!o.success)throw new Error(o.message??"Location lookup failed")
61
- return o}async function m(e,r,a=h){const n=`${a.replace(/\/$/,"")}/v1/digipin/reverse`
62
- let t
63
- try{t=await fetch(n,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":r},body:JSON.stringify({digipin:e}),signal:AbortSignal.timeout(15e3)})}catch(o){if(o instanceof Error&&"TimeoutError"===o.name)throw new Error("Request timed out. Check your internet connection.")
64
- throw new Error(`Network error: ${o instanceof Error?o.message:String(o)}`)}if(!t.ok){let e=""
65
- try{e=await t.text()}catch{}if(401===t.status||403===t.status)throw new Error("Invalid API key. Check your QuantaRoute API key.")
66
- if(429===t.status)throw new Error("Rate limit exceeded. Upgrade your plan or try again later.")
67
- throw new Error(`API error ${t.status}: ${e||t.statusText}`)}const i=await t.json()
63
+ return o}async function f(e,r,n=h){const t=`${n.replace(/\/$/,"")}/v1/digipin/reverse`
64
+ let a
65
+ try{a=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":r},body:JSON.stringify({digipin:e}),signal:p(15e3)})}catch(o){if(o instanceof Error&&("TimeoutError"===o.name||"AbortError"===o.name))throw new Error("Request timed out. Check your internet connection.")
66
+ throw new Error(`Network error: ${o instanceof Error?o.message:String(o)}`)}if(!a.ok){let e=""
67
+ try{e=await a.text()}catch{}if(401===a.status||403===a.status)throw new Error("Invalid API key. Check your QuantaRoute API key.")
68
+ if(429===a.status)throw new Error("Rate limit exceeded. Upgrade your plan or try again later.")
69
+ throw new Error(`API error ${a.status}: ${e||a.statusText}`)}const i=await a.json()
68
70
  if(!i.success)throw new Error(i.message??"Reverse geocoding failed")
69
71
  return i}const g={flatNumber:"",floorNumber:"",buildingName:"",streetName:""}
70
- function f(e,r){switch(r.type){case"LOAD_START":return{status:"loading"}
72
+ function b(e,r){switch(r.type){case"LOAD_START":return{status:"loading"}
71
73
  case"LOAD_SUCCESS":{const e=function(e){const r={}
72
74
  e.name?r.buildingName=e.name:e.building_name?r.buildingName=e.building_name:e.addr_housename&&(r.buildingName=e.addr_housename)
73
- const a=[]
74
- return e.road&&a.push(e.road),e.suburb&&a.push(e.suburb),a.length>0&&(r.streetName=a.join(", ")),r}(r.addressComponents)
75
+ const n=[]
76
+ return e.road&&n.push(e.road),e.suburb&&n.push(e.suburb),n.length>0&&(r.streetName=n.join(", ")),r}(r.addressComponents)
75
77
  return{status:"ready",adminInfo:r.adminInfo,alternatives:r.alternatives||[],selectedLocality:r.adminInfo.locality,fields:{...g,...e},submitting:!1}}case"LOAD_ERROR":return{status:"error",message:r.message}
76
78
  case"SET_FIELD":return"ready"!==e.status?e:{...e,fields:{...e.fields,[r.key]:r.value}}
77
79
  case"SET_LOCALITY":return"ready"!==e.status?e:{...e,selectedLocality:r.locality}
78
80
  case"SUBMIT_START":return"ready"!==e.status?e:{...e,submitting:!0}
79
81
  case"SUBMIT_END":return"ready"!==e.status?e:{...e,submitting:!1}
80
- default:return e}}const q=({digipin:e,lat:n,lng:t,apiKey:i,apiBaseUrl:o,onAddressComplete:l,onBack:s,onError:d})=>{const[c,u]=a.useReducer(f,{status:"loading"}),h=a.useCallback(async()=>{u({type:"LOAD_START"})
81
- try{const[r,a]=await Promise.all([p(n,t,i,o),m(e,i,o)])
82
- u({type:"LOAD_SUCCESS",adminInfo:r.data.administrative_info,alternatives:r.data.alternatives||[],addressComponents:a.data.addressComponents})}catch(r){const e=r instanceof Error?r.message:"Failed to fetch address data."
83
- u({type:"LOAD_ERROR",message:e}),null==d||d(r instanceof Error?r:new Error(e))}},[e,n,t,i,o,d])
84
- a.useEffect(()=>{h()},[h])
85
- const g=a.useCallback(r=>{if(r.preventDefault(),"ready"!==c.status)return
86
- const{adminInfo:a,selectedLocality:i,fields:o}=c,s=[o.flatNumber,o.floorNumber?`Floor ${o.floorNumber}`:"",o.buildingName,o.streetName,i,a.district,a.state,a.pincode].filter(Boolean),d={digipin:e,lat:n,lng:t,state:a.state,district:a.district,division:a.division,locality:i,pincode:a.pincode,delivery:a.delivery,country:a.country??"India",...o,formattedAddress:s.join(", ")}
82
+ default:return e}}const q=({digipin:e,lat:t,lng:a,apiKey:i,apiBaseUrl:o,onAddressComplete:l,onBack:s,onError:d})=>{const[c,u]=n.useReducer(b,{status:"loading"}),h=n.useCallback(async()=>{u({type:"LOAD_START"})
83
+ try{const[r,n]=await Promise.all([m(t,a,i,o),f(e,i,o)])
84
+ u({type:"LOAD_SUCCESS",adminInfo:r.data.administrative_info,alternatives:r.data.alternatives||[],addressComponents:n.data.addressComponents})}catch(r){const e=r instanceof Error?r.message:"Failed to fetch address data."
85
+ u({type:"LOAD_ERROR",message:e}),null==d||d(r instanceof Error?r:new Error(e))}},[e,t,a,i,o,d])
86
+ n.useEffect(()=>{h()},[h])
87
+ const p=n.useCallback(r=>{if(r.preventDefault(),"ready"!==c.status)return
88
+ const{adminInfo:n,selectedLocality:i,fields:o}=c,s=[o.flatNumber,o.floorNumber?`Floor ${o.floorNumber}`:"",o.buildingName,o.streetName,i,n.district,n.state,n.pincode].filter(Boolean),d={digipin:e,lat:t,lng:a,state:n.state,district:n.district,division:n.division,locality:i,pincode:n.pincode,delivery:n.delivery,country:n.country??"India",...o,formattedAddress:s.join(", ")}
87
89
  u({type:"SUBMIT_START"})
88
- try{l(d)}finally{u({type:"SUBMIT_END"})}},[c,e,n,t,l])
89
- return r.jsxs("div",{className:"qr-form-wrapper",children:[r.jsxs("div",{className:"qr-step-header",children:[r.jsx("button",{type:"button",className:"qr-back-btn",onClick:s,"aria-label":"Back to map",children:r.jsx("svg",{"aria-hidden":"true",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2.5",strokeLinecap:"round",strokeLinejoin:"round",children:r.jsx("path",{d:"M19 12H5M12 19l-7-7 7-7"})})}),r.jsx("div",{className:"qr-step-badge",children:"2"}),r.jsxs("div",{className:"qr-step-text",children:[r.jsx("span",{className:"qr-step-title",children:"Add Address Details"}),r.jsx("span",{className:"qr-step-sub",children:"Flat number and building info"})]})]}),r.jsxs("div",{className:"qr-form-digipin-strip",children:[r.jsxs("svg",{"aria-hidden":"true",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[r.jsx("path",{d:"M21 10c0 7-9 13-9 13S3 17 3 10a9 9 0 0118 0z"}),r.jsx("circle",{cx:"12",cy:"10",r:"3"})]}),r.jsx("span",{className:"qr-form-digipin-strip__label",children:"DigiPin"}),r.jsx("span",{className:"qr-form-digipin-strip__code",children:e})]}),"loading"===c.status&&r.jsxs("div",{className:"qr-loading-state","aria-busy":"true","aria-label":"Fetching address details",children:[r.jsx("div",{className:"qr-spinner qr-spinner--lg","aria-hidden":"true"}),r.jsx("p",{className:"qr-loading-state__text",children:"Fetching address details…"})]}),"error"===c.status&&r.jsxs("div",{className:"qr-error-state",role:"alert",children:[r.jsx("div",{className:"qr-error-state__icon","aria-hidden":"true",children:"⚠"}),r.jsx("p",{className:"qr-error-state__msg",children:c.message}),r.jsx("button",{type:"button",className:"qr-btn qr-btn--secondary qr-btn--sm",onClick:()=>{h()},children:"Retry"})]}),"ready"===c.status&&r.jsxs("form",{onSubmit:g,className:"qr-form",noValidate:!0,children:[r.jsxs("fieldset",{className:"qr-fieldset",children:[r.jsxs("legend",{className:"qr-fieldset__legend",children:[r.jsx("span",{className:"qr-fieldset__icon","aria-hidden":"true",children:"📍"}),"Auto-detected from your pin"]}),r.jsxs("div",{className:"qr-auto-grid",children:[r.jsxs("div",{className:"qr-auto-row",children:[r.jsx("span",{className:"qr-auto-row__label",children:"State"}),r.jsx("span",{className:"qr-auto-row__value",children:c.adminInfo.state})]}),r.jsxs("div",{className:"qr-auto-row",children:[r.jsx("span",{className:"qr-auto-row__label",children:"District"}),r.jsx("span",{className:"qr-auto-row__value",children:c.adminInfo.district})]}),r.jsxs("div",{className:"qr-auto-row qr-auto-row--full",children:[r.jsx("span",{className:"qr-auto-row__label",children:"Locality"}),c.alternatives.length>0?r.jsxs("select",{className:"qr-auto-row__select",value:c.selectedLocality,onChange:e=>u({type:"SET_LOCALITY",locality:e.target.value}),"aria-label":"Select locality",children:[r.jsx("option",{value:c.adminInfo.locality,children:c.adminInfo.locality}),c.alternatives.map((e,a)=>r.jsx("option",{value:e.name,children:e.name},a))]}):r.jsx("span",{className:"qr-auto-row__value",children:c.selectedLocality})]}),r.jsxs("div",{className:"qr-auto-row",children:[r.jsx("span",{className:"qr-auto-row__label",children:"Pincode"}),r.jsx("span",{className:"qr-auto-row__value qr-auto-row__value--pin",children:c.adminInfo.pincode})]})]})]}),r.jsxs("fieldset",{className:"qr-fieldset",children:[r.jsxs("legend",{className:"qr-fieldset__legend",children:[r.jsx("span",{className:"qr-fieldset__icon","aria-hidden":"true",children:"🏠"}),"Your details"]}),r.jsxs("div",{className:"qr-fields-grid",children:[r.jsxs("div",{className:"qr-field qr-field--full",children:[r.jsxs("label",{className:"qr-field__label",htmlFor:"qr-flatNumber",children:["Flat / House Number",r.jsx("span",{className:"qr-required","aria-hidden":"true",children:"*"})]}),r.jsx("input",{id:"qr-flatNumber",type:"text",className:"qr-field__input",placeholder:"e.g. 4B, Flat 201, House No. 12",value:c.fields.flatNumber,onChange:e=>u({type:"SET_FIELD",key:"flatNumber",value:e.target.value}),autoComplete:"address-line1",required:!0,"aria-required":"true"})]}),r.jsxs("div",{className:"qr-field",children:[r.jsxs("label",{className:"qr-field__label",htmlFor:"qr-floorNumber",children:["Floor",r.jsx("span",{className:"qr-optional",children:"optional"})]}),r.jsx("input",{id:"qr-floorNumber",type:"text",className:"qr-field__input",placeholder:"e.g. 3rd, Ground",value:c.fields.floorNumber,onChange:e=>u({type:"SET_FIELD",key:"floorNumber",value:e.target.value})})]}),r.jsxs("div",{className:"qr-field",children:[r.jsxs("label",{className:"qr-field__label",htmlFor:"qr-buildingName",children:["Building / Society",r.jsx("span",{className:"qr-optional",children:"optional"})]}),r.jsx("input",{id:"qr-buildingName",type:"text",className:"qr-field__input",placeholder:"e.g. Sunshine Apts, DDA Colony",value:c.fields.buildingName,onChange:e=>u({type:"SET_FIELD",key:"buildingName",value:e.target.value}),autoComplete:"address-line2"})]}),r.jsxs("div",{className:"qr-field qr-field--full",children:[r.jsxs("label",{className:"qr-field__label",htmlFor:"qr-streetName",children:["Street / Area",r.jsx("span",{className:"qr-optional",children:"optional"})]}),r.jsx("input",{id:"qr-streetName",type:"text",className:"qr-field__input",placeholder:"e.g. MG Road, Sector 12",value:c.fields.streetName,onChange:e=>u({type:"SET_FIELD",key:"streetName",value:e.target.value}),autoComplete:"address-level3"})]})]})]}),r.jsxs("div",{className:"qr-form-actions",children:[r.jsxs("button",{type:"button",className:"qr-btn qr-btn--ghost",onClick:s,children:[r.jsx("svg",{"aria-hidden":"true",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:r.jsx("path",{d:"M19 12H5M12 19l-7-7 7-7"})}),"Adjust Pin"]}),r.jsx("button",{type:"submit",className:"qr-btn qr-btn--primary qr-btn--grow",disabled:c.submitting||!c.fields.flatNumber.trim(),children:c.submitting?r.jsxs(r.Fragment,{children:[r.jsx("span",{className:"qr-spinner qr-spinner--sm","aria-hidden":"true"}),"Saving…"]}):r.jsxs(r.Fragment,{children:["Save Address",r.jsx("svg",{"aria-hidden":"true",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2.5",strokeLinecap:"round",strokeLinejoin:"round",children:r.jsx("path",{d:"M20 6L9 17l-5-5"})})]})})]})]})]})},b=({address:e,onEditAddress:a})=>r.jsxs("div",{className:"qr-success",children:[r.jsx("div",{className:"qr-success__icon","aria-hidden":"true",children:r.jsxs("svg",{viewBox:"0 0 52 52",fill:"none",xmlns:"http://www.w3.org/2000/svg",children:[r.jsx("circle",{cx:"26",cy:"26",r:"26",fill:"var(--qr-success, #10b981)",opacity:"0.12"}),r.jsx("circle",{cx:"26",cy:"26",r:"20",fill:"var(--qr-success, #10b981)"}),r.jsx("path",{d:"M15 26l8 8 14-16",stroke:"white",strokeWidth:"3",strokeLinecap:"round",strokeLinejoin:"round"})]})}),r.jsx("h3",{className:"qr-success__title",children:"Address Saved!"}),r.jsx("p",{className:"qr-success__address",children:e.formattedAddress}),r.jsx("div",{className:"qr-success__meta",children:r.jsxs("span",{className:"qr-digipin-badge qr-digipin-badge--inline",children:[r.jsx("span",{className:"qr-digipin-badge__label",children:"DigiPin"}),r.jsx("span",{className:"qr-digipin-badge__code",children:e.digipin})]})}),r.jsx("button",{type:"button",className:"qr-btn qr-btn--ghost qr-btn--sm",onClick:a,children:"Change Address"})]}),y=({apiKey:e,apiBaseUrl:n="https://api.quantaroute.com",onComplete:t,onError:i,defaultLat:o,defaultLng:l,theme:s="light",className:d="",style:c,mapHeight:h="380px",title:p="Add Delivery Address",indiaBoundaryUrl:m})=>{const[g,f]=a.useState("map"),[y,N]=a.useState(null),[v,_]=a.useState(null)
90
- return r.jsxs("div",{className:`qr-checkout qr-checkout--${s} ${d}`,style:c,"data-testid":"quantaroute-checkout",children:["done"!==g&&r.jsxs("div",{className:"qr-header",children:[r.jsxs("div",{className:"qr-header__brand",children:[r.jsxs("svg",{className:"qr-header__logo","aria-hidden":"true",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[r.jsx("path",{d:"M21 10c0 7-9 13-9 13S3 17 3 10a9 9 0 0118 0z"}),r.jsx("circle",{cx:"12",cy:"10",r:"3"})]}),r.jsx("span",{className:"qr-header__title",children:p})]}),r.jsxs("div",{className:"qr-progress","aria-label":"Progress: step ${ step === 'map' ? 1 : 2 } of 2",role:"progressbar",children:[r.jsx("div",{className:"qr-progress__dot "+("map"===g||"form"===g||"done"===g?"qr-progress__dot--active":"")}),r.jsx("div",{className:"qr-progress__line "+("form"===g||"done"===g?"qr-progress__line--active":"")}),r.jsx("div",{className:"qr-progress__dot "+("form"===g||"done"===g?"qr-progress__dot--active":"")})]})]}),"map"===g&&r.jsx(u,{onLocationConfirm:(e,r,a)=>{N({lat:e,lng:r,digipin:a}),f("form")},defaultLat:o,defaultLng:l,mapHeight:h,theme:s,indiaBoundaryUrl:m}),"form"===g&&y&&r.jsx(q,{digipin:y.digipin,lat:y.lat,lng:y.lng,apiKey:e,apiBaseUrl:n,onAddressComplete:e=>{_(e),f("done"),t(e)},onBack:()=>{f("map")},onError:i,theme:s}),"done"===g&&v&&r.jsx(b,{address:v,onEditAddress:()=>{f("map"),N(null),_(null)}}),r.jsxs("div",{className:"qr-footer",children:[r.jsx("span",{children:"Powered by"}),r.jsx("a",{href:"https://quantaroute.com",target:"_blank",rel:"noopener noreferrer",className:"qr-footer__link",children:"QuantaRoute"}),r.jsx("span",{className:"qr-footer__flag","aria-label":"Made in India",children:"🇮🇳"})]})]})}
91
- e.AddressForm=q,e.CheckoutWidget=y,e.DIGIPIN_BOUNDS=i,e.MapPinSelector=u,e.default=y,e.getDigiPin=o,e.getLatLngFromDigiPin=l,e.isValidDigiPin=function(e){if(!e||"string"!=typeof e)return!1
90
+ try{l(d)}finally{u({type:"SUBMIT_END"})}},[c,e,t,a,l])
91
+ return r.jsxs("div",{className:"qr-form-wrapper",children:[r.jsxs("div",{className:"qr-step-header",children:[r.jsx("button",{type:"button",className:"qr-back-btn",onClick:s,"aria-label":"Back to map",children:r.jsx("svg",{"aria-hidden":"true",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2.5",strokeLinecap:"round",strokeLinejoin:"round",children:r.jsx("path",{d:"M19 12H5M12 19l-7-7 7-7"})})}),r.jsx("div",{className:"qr-step-badge",children:"2"}),r.jsxs("div",{className:"qr-step-text",children:[r.jsx("span",{className:"qr-step-title",children:"Add Address Details"}),r.jsx("span",{className:"qr-step-sub",children:"Flat number and building info"})]})]}),r.jsxs("div",{className:"qr-form-digipin-strip",children:[r.jsxs("svg",{"aria-hidden":"true",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[r.jsx("path",{d:"M21 10c0 7-9 13-9 13S3 17 3 10a9 9 0 0118 0z"}),r.jsx("circle",{cx:"12",cy:"10",r:"3"})]}),r.jsx("span",{className:"qr-form-digipin-strip__label",children:"DigiPin"}),r.jsx("span",{className:"qr-form-digipin-strip__code",children:e})]}),"loading"===c.status&&r.jsxs("div",{className:"qr-loading-state","aria-busy":"true","aria-label":"Fetching address details",children:[r.jsx("div",{className:"qr-spinner qr-spinner--lg","aria-hidden":"true"}),r.jsx("p",{className:"qr-loading-state__text",children:"Fetching address details…"})]}),"error"===c.status&&r.jsxs("div",{className:"qr-error-state",role:"alert",children:[r.jsx("div",{className:"qr-error-state__icon","aria-hidden":"true",children:"⚠"}),r.jsx("p",{className:"qr-error-state__msg",children:c.message}),r.jsx("button",{type:"button",className:"qr-btn qr-btn--secondary qr-btn--sm",onClick:()=>{h()},children:"Retry"})]}),"ready"===c.status&&r.jsxs("form",{onSubmit:p,className:"qr-form",noValidate:!0,children:[r.jsxs("fieldset",{className:"qr-fieldset",children:[r.jsxs("legend",{className:"qr-fieldset__legend",children:[r.jsx("span",{className:"qr-fieldset__icon","aria-hidden":"true",children:"📍"}),"Auto-detected from your pin"]}),r.jsxs("div",{className:"qr-auto-grid",children:[r.jsxs("div",{className:"qr-auto-row",children:[r.jsx("span",{className:"qr-auto-row__label",children:"State"}),r.jsx("span",{className:"qr-auto-row__value",children:c.adminInfo.state})]}),r.jsxs("div",{className:"qr-auto-row",children:[r.jsx("span",{className:"qr-auto-row__label",children:"District"}),r.jsx("span",{className:"qr-auto-row__value",children:c.adminInfo.district})]}),r.jsxs("div",{className:"qr-auto-row qr-auto-row--full",children:[r.jsx("span",{className:"qr-auto-row__label",children:"Locality"}),c.alternatives.length>0?r.jsxs("select",{className:"qr-auto-row__select",value:c.selectedLocality,onChange:e=>u({type:"SET_LOCALITY",locality:e.target.value}),"aria-label":"Select locality",children:[r.jsx("option",{value:c.adminInfo.locality,children:c.adminInfo.locality}),c.alternatives.map((e,n)=>r.jsx("option",{value:e.name,children:e.name},n))]}):r.jsx("span",{className:"qr-auto-row__value",children:c.selectedLocality})]}),r.jsxs("div",{className:"qr-auto-row",children:[r.jsx("span",{className:"qr-auto-row__label",children:"Pincode"}),r.jsx("span",{className:"qr-auto-row__value qr-auto-row__value--pin",children:c.adminInfo.pincode})]})]})]}),r.jsxs("fieldset",{className:"qr-fieldset",children:[r.jsxs("legend",{className:"qr-fieldset__legend",children:[r.jsx("span",{className:"qr-fieldset__icon","aria-hidden":"true",children:"🏠"}),"Your details"]}),r.jsxs("div",{className:"qr-fields-grid",children:[r.jsxs("div",{className:"qr-field qr-field--full",children:[r.jsxs("label",{className:"qr-field__label",htmlFor:"qr-flatNumber",children:["Flat / House Number",r.jsx("span",{className:"qr-required","aria-hidden":"true",children:"*"})]}),r.jsx("input",{id:"qr-flatNumber",type:"text",className:"qr-field__input",placeholder:"e.g. 4B, Flat 201, House No. 12",value:c.fields.flatNumber,onChange:e=>u({type:"SET_FIELD",key:"flatNumber",value:e.target.value}),autoComplete:"address-line1",required:!0,"aria-required":"true"})]}),r.jsxs("div",{className:"qr-field",children:[r.jsxs("label",{className:"qr-field__label",htmlFor:"qr-floorNumber",children:["Floor",r.jsx("span",{className:"qr-optional",children:"optional"})]}),r.jsx("input",{id:"qr-floorNumber",type:"text",className:"qr-field__input",placeholder:"e.g. 3rd, Ground",value:c.fields.floorNumber,onChange:e=>u({type:"SET_FIELD",key:"floorNumber",value:e.target.value})})]}),r.jsxs("div",{className:"qr-field",children:[r.jsxs("label",{className:"qr-field__label",htmlFor:"qr-buildingName",children:["Building / Society",r.jsx("span",{className:"qr-optional",children:"optional"})]}),r.jsx("input",{id:"qr-buildingName",type:"text",className:"qr-field__input",placeholder:"e.g. Sunshine Apts, DDA Colony",value:c.fields.buildingName,onChange:e=>u({type:"SET_FIELD",key:"buildingName",value:e.target.value}),autoComplete:"address-line2"})]}),r.jsxs("div",{className:"qr-field qr-field--full",children:[r.jsxs("label",{className:"qr-field__label",htmlFor:"qr-streetName",children:["Street / Area",r.jsx("span",{className:"qr-optional",children:"optional"})]}),r.jsx("input",{id:"qr-streetName",type:"text",className:"qr-field__input",placeholder:"e.g. MG Road, Sector 12",value:c.fields.streetName,onChange:e=>u({type:"SET_FIELD",key:"streetName",value:e.target.value}),autoComplete:"address-level3"})]})]})]}),r.jsxs("div",{className:"qr-form-actions",children:[r.jsxs("button",{type:"button",className:"qr-btn qr-btn--ghost",onClick:s,children:[r.jsx("svg",{"aria-hidden":"true",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:r.jsx("path",{d:"M19 12H5M12 19l-7-7 7-7"})}),"Adjust Pin"]}),r.jsx("button",{type:"submit",className:"qr-btn qr-btn--primary qr-btn--grow",disabled:c.submitting||!c.fields.flatNumber.trim(),children:c.submitting?r.jsxs(r.Fragment,{children:[r.jsx("span",{className:"qr-spinner qr-spinner--sm","aria-hidden":"true"}),"Saving…"]}):r.jsxs(r.Fragment,{children:["Save Address",r.jsx("svg",{"aria-hidden":"true",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2.5",strokeLinecap:"round",strokeLinejoin:"round",children:r.jsx("path",{d:"M20 6L9 17l-5-5"})})]})})]})]})]})},y=({address:e,onEditAddress:n})=>r.jsxs("div",{className:"qr-success",children:[r.jsx("div",{className:"qr-success__icon","aria-hidden":"true",children:r.jsxs("svg",{viewBox:"0 0 52 52",fill:"none",xmlns:"http://www.w3.org/2000/svg",children:[r.jsx("circle",{cx:"26",cy:"26",r:"26",fill:"var(--qr-success, #10b981)",opacity:"0.12"}),r.jsx("circle",{cx:"26",cy:"26",r:"20",fill:"var(--qr-success, #10b981)"}),r.jsx("path",{d:"M15 26l8 8 14-16",stroke:"white",strokeWidth:"3",strokeLinecap:"round",strokeLinejoin:"round"})]})}),r.jsx("h3",{className:"qr-success__title",children:"Address Saved!"}),r.jsx("p",{className:"qr-success__address",children:e.formattedAddress}),r.jsx("div",{className:"qr-success__meta",children:r.jsxs("span",{className:"qr-digipin-badge qr-digipin-badge--inline",children:[r.jsx("span",{className:"qr-digipin-badge__label",children:"DigiPin"}),r.jsx("span",{className:"qr-digipin-badge__code",children:e.digipin})]})}),r.jsx("button",{type:"button",className:"qr-btn qr-btn--ghost qr-btn--sm",onClick:n,children:"Change Address"})]}),N=({apiKey:e,apiBaseUrl:t="https://api.quantaroute.com",onComplete:a,onError:i,defaultLat:o,defaultLng:l,theme:s="light",className:d="",style:c,mapHeight:h="380px",title:p="Add Delivery Address",indiaBoundaryUrl:m})=>{const[f,g]=n.useState("map"),[b,N]=n.useState(null),[v,_]=n.useState(null)
92
+ return r.jsxs("div",{className:`qr-checkout qr-checkout--${s} ${d}`,style:c,"data-testid":"quantaroute-checkout",children:["done"!==f&&r.jsxs("div",{className:"qr-header",children:[r.jsxs("div",{className:"qr-header__brand",children:[r.jsxs("svg",{className:"qr-header__logo","aria-hidden":"true",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[r.jsx("path",{d:"M21 10c0 7-9 13-9 13S3 17 3 10a9 9 0 0118 0z"}),r.jsx("circle",{cx:"12",cy:"10",r:"3"})]}),r.jsx("span",{className:"qr-header__title",children:p})]}),r.jsxs("div",{className:"qr-progress","aria-label":"Progress: step ${ step === 'map' ? 1 : 2 } of 2",role:"progressbar",children:[r.jsx("div",{className:"qr-progress__dot "+("map"===f||"form"===f||"done"===f?"qr-progress__dot--active":"")}),r.jsx("div",{className:"qr-progress__line "+("form"===f||"done"===f?"qr-progress__line--active":"")}),r.jsx("div",{className:"qr-progress__dot "+("form"===f||"done"===f?"qr-progress__dot--active":"")})]})]}),"map"===f&&r.jsx(u,{onLocationConfirm:(e,r,n)=>{N({lat:e,lng:r,digipin:n}),g("form")},defaultLat:o,defaultLng:l,mapHeight:h,theme:s,indiaBoundaryUrl:m}),"form"===f&&b&&r.jsx(q,{digipin:b.digipin,lat:b.lat,lng:b.lng,apiKey:e,apiBaseUrl:t,onAddressComplete:e=>{_(e),g("done"),a(e)},onBack:()=>{g("map")},onError:i,theme:s}),"done"===f&&v&&r.jsx(y,{address:v,onEditAddress:()=>{g("map"),N(null),_(null)}}),r.jsxs("div",{className:"qr-footer",children:[r.jsx("span",{children:"Powered by"}),r.jsx("a",{href:"https://quantaroute.com",target:"_blank",rel:"noopener noreferrer",className:"qr-footer__link",children:"QuantaRoute"}),r.jsx("span",{className:"qr-footer__flag","aria-label":"Made in India",children:"🇮🇳"})]})]})}
93
+ e.AddressForm=q,e.CheckoutWidget=N,e.DIGIPIN_BOUNDS=i,e.MapPinSelector=u,e.default=N,e.getDigiPin=o,e.getLatLngFromDigiPin=l,e.isValidDigiPin=function(e){if(!e||"string"!=typeof e)return!1
92
94
  if(!/^[A-Z0-9]{3}-[A-Z0-9]{3}-[A-Z0-9]{4}$/i.test(e.trim()))return!1
93
- try{return l(e.trim().toUpperCase()),!0}catch{return!1}},e.isWithinIndia=s,e.lookupLocation=p,e.reverseGeocode=m,e.useDigiPin=function(e,r){return a.useMemo(()=>{if(!s(e,r))return null
95
+ try{return l(e.trim().toUpperCase()),!0}catch{return!1}},e.isWithinIndia=s,e.lookupLocation=m,e.reverseGeocode=f,e.useDigiPin=function(e,r){return n.useMemo(()=>{if(!s(e,r))return null
94
96
  try{return o(e,r)}catch{return null}},[e,r])},e.useGeolocation=d,Object.defineProperties(e,{t:{value:!0},[Symbol.toStringTag]:{value:"Module"}})})
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quantaroute/checkout",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
4
4
  "description": "Embeddable DigiPin-powered smart address checkout widget — React (web), React Native & Expo (iOS/Android)",
5
5
  "main": "./dist/lib/quantaroute-checkout.umd.js",
6
6
  "module": "./dist/lib/quantaroute-checkout.es.js",
@@ -3,12 +3,11 @@ import {
3
3
  View,
4
4
  Text,
5
5
  TouchableOpacity,
6
- ActivityIndicator,
7
6
  } from 'react-native';
8
- import { OSMView } from 'expo-osm-sdk';
7
+ import { OSMView, LocationButton } from 'expo-osm-sdk';
9
8
  import type { OSMViewRef, MarkerConfig } from 'expo-osm-sdk';
9
+ import * as Location from 'expo-location';
10
10
  import { getDigiPin, isWithinIndia } from '../core/digipin';
11
- import { useGeolocation } from '../hooks/useGeolocation.native';
12
11
  import type { MapPinSelectorProps } from '../core/types';
13
12
  import { styles, COLORS } from '../styles/checkout.native';
14
13
 
@@ -75,8 +74,7 @@ const MapPinSelector: React.FC<MapPinSelectorProps> = ({
75
74
  computeDigiPinSafe(initLat, initLng)
76
75
  );
77
76
  const [mapReady, setMapReady] = useState(false);
78
-
79
- const { loading: geoLoading, error: geoError, locate, clearError } = useGeolocation();
77
+ const [locateError, setLocateError] = useState<string | null>(null);
80
78
 
81
79
  const isDark = theme === 'dark';
82
80
 
@@ -107,14 +105,18 @@ const MapPinSelector: React.FC<MapPinSelectorProps> = ({
107
105
  },
108
106
  ];
109
107
 
110
- // ── Locate-me handler ────────────────────────────────────────────────────────
111
- const handleLocateMe = useCallback(() => {
112
- clearError();
113
- locate((lat, lng) => {
114
- handleCoordChange(lat, lng);
115
- void mapRef.current?.animateToLocation(lat, lng, STREET_ZOOM);
108
+ // ── Locate-me: delegate to expo-osm-sdk's LocationButton via getCurrentLocation ──
109
+ const getCurrentLocation = useCallback(async () => {
110
+ setLocateError(null);
111
+ const { status } = await Location.requestForegroundPermissionsAsync();
112
+ if (status !== Location.PermissionStatus.GRANTED) {
113
+ throw new Error('Location permission denied');
114
+ }
115
+ const loc = await Location.getCurrentPositionAsync({
116
+ accuracy: Location.Accuracy.High,
116
117
  });
117
- }, [locate, clearError, handleCoordChange]);
118
+ return { latitude: loc.coords.latitude, longitude: loc.coords.longitude };
119
+ }, []);
118
120
 
119
121
  // ── Confirm handler ──────────────────────────────────────────────────────────
120
122
  const handleConfirm = useCallback(() => {
@@ -186,31 +188,26 @@ const MapPinSelector: React.FC<MapPinSelectorProps> = ({
186
188
  </View>
187
189
  ) : null}
188
190
 
189
- {/* Locate-me button */}
190
- <TouchableOpacity
191
- style={[styles.locateBtn, isDark && styles.locateBtnDark]}
192
- onPress={handleLocateMe}
193
- disabled={geoLoading}
194
- activeOpacity={0.7}
195
- accessibilityLabel="Use my current location"
196
- accessibilityRole="button"
197
- >
198
- {geoLoading ? (
199
- <ActivityIndicator size="small" color={COLORS.primary} />
200
- ) : (
201
- <Text style={{ fontSize: 20, color: isDark ? COLORS.textDark : COLORS.text }}>
202
-
203
- </Text>
204
- )}
205
- </TouchableOpacity>
191
+ {/* Locate-me button — expo-osm-sdk's built-in LocationButton */}
192
+ <LocationButton
193
+ style={styles.locateBtn}
194
+ color={COLORS.primary}
195
+ size={44}
196
+ getCurrentLocation={getCurrentLocation}
197
+ onLocationFound={({ latitude, longitude }) => {
198
+ handleCoordChange(latitude, longitude);
199
+ void mapRef.current?.animateToLocation(latitude, longitude, STREET_ZOOM);
200
+ }}
201
+ onLocationError={(err) => setLocateError(err)}
202
+ />
206
203
 
207
204
  {/* Geo error toast */}
208
- {geoError ? (
205
+ {locateError ? (
209
206
  <View style={styles.geoError}>
210
- <Text style={styles.geoErrorText}>{geoError}</Text>
207
+ <Text style={styles.geoErrorText}>{locateError}</Text>
211
208
  <TouchableOpacity
212
209
  style={styles.geoErrorDismiss}
213
- onPress={clearError}
210
+ onPress={() => setLocateError(null)}
214
211
  accessibilityLabel="Dismiss error"
215
212
  >
216
213
  <Text style={styles.geoErrorDismissText}>×</Text>
package/src/core/api.ts CHANGED
@@ -2,6 +2,23 @@ import type { LocationLookupResponse } from './types';
2
2
 
3
3
  const DEFAULT_BASE_URL = 'https://api.quantaroute.com';
4
4
 
5
+ /**
6
+ * Cross-platform timeout signal.
7
+ * AbortSignal.timeout() is not available in React Native / Hermes — fall back
8
+ * to a manual AbortController + setTimeout when it is missing.
9
+ */
10
+ function makeTimeoutSignal(ms: number): AbortSignal {
11
+ if (
12
+ typeof AbortSignal !== 'undefined' &&
13
+ typeof (AbortSignal as { timeout?: unknown }).timeout === 'function'
14
+ ) {
15
+ return AbortSignal.timeout(ms);
16
+ }
17
+ const ctrl = new AbortController();
18
+ setTimeout(() => ctrl.abort(), ms);
19
+ return ctrl.signal;
20
+ }
21
+
5
22
  /**
6
23
  * Address components from Nominatim/OpenStreetMap (via reverse geocoding).
7
24
  */
@@ -61,10 +78,10 @@ export async function lookupLocation(
61
78
  'x-api-key': apiKey,
62
79
  },
63
80
  body: JSON.stringify({ latitude: lat, longitude: lng }),
64
- signal: AbortSignal.timeout(15_000), // 15s timeout
81
+ signal: makeTimeoutSignal(15_000),
65
82
  });
66
83
  } catch (err) {
67
- if (err instanceof Error && err.name === 'TimeoutError') {
84
+ if (err instanceof Error && (err.name === 'TimeoutError' || err.name === 'AbortError')) {
68
85
  throw new Error('Request timed out. Check your internet connection.');
69
86
  }
70
87
  throw new Error(`Network error: ${err instanceof Error ? err.message : String(err)}`);
@@ -115,10 +132,10 @@ export async function reverseGeocode(
115
132
  'x-api-key': apiKey,
116
133
  },
117
134
  body: JSON.stringify({ digipin }),
118
- signal: AbortSignal.timeout(15_000), // 15s timeout
135
+ signal: makeTimeoutSignal(15_000),
119
136
  });
120
137
  } catch (err) {
121
- if (err instanceof Error && err.name === 'TimeoutError') {
138
+ if (err instanceof Error && (err.name === 'TimeoutError' || err.name === 'AbortError')) {
122
139
  throw new Error('Request timed out. Check your internet connection.');
123
140
  }
124
141
  throw new Error(`Network error: ${err instanceof Error ? err.message : String(err)}`);