1var V=Object.defineProperty;var Y=(t,n,a)=>n in t?V(t,n,{enumerable:!0,configurable:!0,writable:!0,value:a}):t[n]=a;var B=(t,n,a)=>Y(t,typeof n!="symbol"?n+"":n,a);import{_ as J}from"./vendor-supabase-RijQlDjE.js";import{r as s,j as e}from"./vendor-react-Dqm4a0k5.js";import{S as Q,b as z,s as G,g as N,C as R,e as T,f as W,h as U,a as F,B as O,l as X,c as j}from"./index-BR-ShKyk.js";import{a as M}from"./use-toast-DL6cPnZQ.js";import{bY as Z,ak as ee,ae as q,V as te,aj as L,cE as ne,cF as H,aV as se}from"./vendor-icons-Dv6Y7wXg.js";import{m as D,A as ae}from"./vendor-motion-uI1IlCbt.js";import"./vendor-utils-KLhAiGEE.js";import"./vendor-query-BQV4-hhd.js";import"./radix-react-slot-CrO6XSQS.js";import"./radix-react-compose-refs-IIetlP92.js";const x=class x{constructor(){B(this,"capabilities");this.capabilities={hasStatusBar:!1,hasSplashScreen:!1,hasHaptics:"vibrate"in navigator,platform:"web"}}static getInstance(){return x.instance||(x.instance=new x),x.instance}async setStatusBarStyle(n){}async hideStatusBar(){}async showStatusBar(){}async hideSplashScreen(){}async triggerHaptic(n){if(this.capabilities.hasHaptics)try{const a={light:[10],medium:[20],heavy:[50],selection:[5],success:[10,50,10],warning:[20,100,20],error:[100,50,100]};navigator.vibrate(a[n]??[10])}catch(a){console.warn("Failed to trigger haptic feedback:",a)}}getSafeAreaInsets(){const n=getComputedStyle(document.documentElement);return{top:parseInt(n.getPropertyValue("--safe-area-inset-top")||"0",10),bottom:parseInt(n.getPropertyValue("--safe-area-inset-bottom")||"0",10),left:parseInt(n.getPropertyValue("--safe-area-inset-left")||"0",10),right:parseInt(n.getPropertyValue("--safe-area-inset-right")||"0",10)}}isOnline(){return navigator.onLine}getDeviceInfo(){return{platform:this.capabilities.platform,userAgent:navigator.userAgent,screenWidth:screen.width,screenHeight:screen.height,pixelRatio:window.devicePixelRatio||1,orientation:screen.orientation?.type||"unknown"}}};B(x,"instance");let $=x;function ie(){const[t]=s.useState(()=>$.getInstance()),[n,a]=s.useState(navigator.onLine);return s.useEffect(()=>{const d=()=>a(!0),g=()=>a(!1);return window.addEventListener("online",d),window.addEventListener("offline",g),()=>{window.removeEventListener("online",d),window.removeEventListener("offline",g)}},[]),{capabilities:t.capabilities,isOnline:n,deviceInfo:t.getDeviceInfo(),safeAreaInsets:t.getSafeAreaInsets(),setStatusBarStyle:t.setStatusBarStyle.bind(t),hideStatusBar:t.hideStatusBar.bind(t),showStatusBar:t.showStatusBar.bind(t),hideSplashScreen:t.hideSplashScreen.bind(t),triggerHaptic:t.triggerHaptic.bind(t)}}const P="dealkeep_sync_queue",re=3;function k(){const[t,n]=s.useState([]),[a,d]=s.useState(!1),[g,m]=s.useState(navigator.onLine),S=s.useRef(!1),b=s.useRef(t);b.current=t;const{toast:w}=M();s.useEffect(()=>{const r=localStorage.getItem(P);if(r)try{n(JSON.parse(r))}catch(i){console.error("[BackgroundSync] Failed to parse queue:",i)}},[]),s.useEffect(()=>{localStorage.setItem(P,JSON.stringify(t))},[t]);const I=async r=>{console.log("[BackgroundSync] Processing action:",r.type,r.table);try{const{table:i,type:u,data:c}=r,l=Q,o=z,{data:h}=await G.auth.getSession(),y=h?.session?.access_token;if(!y)return console.error("[BackgroundSync] No user session available, skipping action"),!1;const A={"Content-Type":"application/json",apikey:o,Authorization:`Bearer ${y}`};switch(u){case"create":{const v=await fetch(`${l}/rest/v1/${i}`,{method:"POST",headers:A,body:JSON.stringify(c)});if(!v.ok)throw new Error(`Create failed: ${v.status}`);break}case"update":{const{id:v,...K}=c,_=await fetch(`${l}/rest/v1/${i}?id=eq.${v}`,{method:"PATCH",headers:A,body:JSON.stringify(K)});if(!_.ok)throw new Error(`Update failed: ${_.status}`);break}case"delete":{const v=await fetch(`${l}/rest/v1/${i}?id=eq.${c.id}`,{method:"DELETE",headers:A});if(!v.ok)throw new Error(`Delete failed: ${v.status}`);break}}return console.log("[BackgroundSync] Action completed successfully"),!0}catch(i){return console.error("[BackgroundSync] Action failed:",i),!1}},f=s.useCallback(async()=>{const r=b.current;if(S.current||r.length===0||!navigator.onLine)return;
1S.current=!0,d(!0),console.log("[BackgroundSync] Starting sync of",r.length,"actions");const i=[];let u=0,c=0;for(const l of r)await I(l)?u++:l.retries<re?(i.push({...l,retries:l.retries+1}),c++):console.log("[BackgroundSync] Max retries reached, dropping action:",l.id);n(i),d(!1),S.current=!1,u>0&&w({title:"Changes Synced",description:`${u} pending change${u>1?"s":""} synced successfully.`}),c>0&&w({title:"Some Changes Failed",description:`${c} change${c>1?"s":""} will be retried later.`,variant:"destructive"})},[w]);s.useEffect(()=>{const r=()=>{console.log("[BackgroundSync] Connection restored"),m(!0),f()},i=()=>{console.log("[BackgroundSync] Connection lost"),m(!1)};return window.addEventListener("online",r),window.addEventListener("offline",i),navigator.onLine&&t.length>0&&f(),()=>{window.removeEventListener("online",r),window.removeEventListener("offline",i)}},[f,t.length]),s.useEffect(()=>{const r=async()=>{if("serviceWorker"in navigator&&"SyncManager"in window)try{await(await navigator.serviceWorker.ready).sync.register("dealkeep-sync"),console.log("[BackgroundSync] Background sync registered")}catch(i){console.log("[BackgroundSync] Background sync registration failed:",i)}};t.length>0&&r()},[t.length]);const E=s.useCallback((r,i,u)=>{const c={id:crypto.randomUUID(),type:r,table:i,data:u,timestamp:Date.now(),retries:0};return console.log("[BackgroundSync] Queuing action:",r,i),n(l=>[...l,c]),navigator.onLine&&setTimeout(f,100),c.id},[f]),p=s.useCallback(r=>{n(i=>i.filter(u=>u.id!==r))},[]),C=s.useCallback(()=>{n([]),localStorage.removeItem(P)},[]);return{isOnline:g,isSyncing:a,pendingCount:t.length,pendingActions:t,queueAction:E,removeAction:p,clearQueue:C,syncAll:f}}const oe=X(()=>J(()=>Promise.resolve().then(()=>ue),void 0).then(t=>({default:t.OfflineIndicator})),{onFinalFailure:"resolveNull"});function je({showInstallPrompt:t=!0,className:n}){const[a,d]=s.useState(null),[g,m]=s.useState(!1),[S,b]=s.useState(!1),[w,I]=s.useState(!1),[f,E]=s.useState(null),{toast:p}=M(),{capabilities:C}=ie(),{isOnline:r,pendingCount:i}=k();s.useEffect(()=>{(()=>{const h=window.matchMedia("(display-mode: standalone)").matches,y=window.navigator.standalone===!0;b(h||y)})()},[]),s.useEffect(()=>{const o=y=>{y.preventDefault(),d(y),m(!0)},h=()=>{b(!0),m(!1),d(null),p({title:"App Installed!",description:"DealKeep has been installed on your device."})};return window.addEventListener("beforeinstallprompt",o),window.addEventListener("appinstalled",h),()=>{window.removeEventListener("beforeinstallprompt",o),window.removeEventListener("appinstalled",h)}},[p]),s.useEffect(()=>{"serviceWorker"in navigator&&navigator.serviceWorker.ready.then(o=>{E(o),o.addEventListener("updatefound",()=>{const h=o.installing;h&&h.addEventListener("statechange",()=>{h.state==="installed"&&navigator.serviceWorker.controller&&I(!0)})})})},[]);const u=s.useCallback(async()=>{if(a)try{await a.prompt();const{outcome:o}=await a.userChoice;o==="accepted"&&p({title:"Installing...",description:"The app is being installed on your device."}),d(null),m(!1)}catch(o){console.error("Install failed:",o),p({title:"Installation Failed",description:"Unable to install the app. Please try again.",variant:"destructive"})}},[a,p]),c=s.useCallback(()=>{f&&f.waiting&&(f.waiting.postMessage({type:"SKIP_WAITING"}),window.location.reload())},[f]),l=s.useCallback(async()=>{if(!("Notification"in window)){p({title:"Not Supported",description:"Notifications are not supported on this device.",variant:"destructive"});return}try{const o=await Notification.requestPermission();p(o==="granted"?{title:"Notifications Enabled",description:"You'll receive important updates about your deals."}:{title:"Notifications Disabled",description:"You can enable them later in your browser settings.",variant:"destructive"})}catch(o){console.error("Notification permission failed:",o)}},[p]);return!t&&!w&&!g?null:e.jsxs("div",{className:n,children:[e.jsxs("div",{className:"flex items-center gap-2 mb-4",children:[r?e.jsxs(e.Fragment,{children:[e.jsx(Z,{className:"h-4 w-4 text-success"}),e.jsx(N,{variant:"secondary",className:"text-success",children:"Online"})]}):e.jsx(s.Suspense,{fallback:null,children:e.jsx(oe,{showPendingCount:!0})}),i>0&&r&&e.jsxs(N,{variant:"outline",className:"text-muted-foreground",children:[e.jsx(ee,{className:"h-3 w-3 mr-1"}),i," to sync"]})]}),g&&t&&e.jsxs(R,{className:"mb-4",children:[e.jsxs(T,{children:[e.jsxs(W,{className:"flex items-center gap-2",children:[e.jsx(q,{className:"h-5 w-5"}),"Install DealKeep"]}),e.jsx(U,{children:"Install the app for a better experience with offline access and notifications."})]}),e.jsx(F,{children:e.jsxs("div",{className:"flex flex-col gap-2",children:[e.jsxs(O,{onClick:u,className:"w-full",children:[e.jsx(q,{className:"h-4 w-4 mr-2"}),"Install App"]}),C.platform!=="web"&&e.jsxs(O,{variant:"outline",onClick:l,className:"w-full",children:[e.jsx(te,{className:"h-4 w-4 mr-2"}),"Enable Notifications"]})]})})]}),w&&e.jsxs(R,{className:"mb-4",children:[e.jsxs(T,{children:[e.jsxs(W,{className:"flex items-center gap-2",children:[e.jsx(L,{className:"h-5 w-5"}),"Update Available"]}),e.jsx(U,{children:"A new version of DealKeep is ready to install."})]}),e.jsx(F,{children:e.jsxs(O,{onClick:c,className:"w-full",children:[e.jsx(L,{className:"h-4 w-4 mr-2"}
1),"Update Now"]})})]}),S&&e.jsx("div",{className:"flex items-center gap-2",children:e.jsx(N,{variant:"secondary",className:"text-success",children:"App Installed"})})]})}function ce({className:t,showPendingCount:n=!0,compact:a=!1}){const{isOnline:d,pendingCount:g,isSyncing:m}=k();return e.jsxs(ae,{mode:"wait",children:[!d&&e.jsxs(D.div,{initial:{opacity:0,y:-20},animate:{opacity:1,y:0},exit:{opacity:0,y:-20},className:j("flex items-center gap-2 px-3 py-2 rounded-lg bg-destructive/10 border border-destructive/20",a&&"px-2 py-1",t),children:[e.jsx(se,{className:j("text-destructive",a?"h-3 w-3":"h-4 w-4")}),!a&&e.jsx("span",{className:"text-sm text-destructive font-medium",children:"Offline Mode"}),n&&g>0&&e.jsxs(N,{variant:"destructive",className:"text-xs",children:[g," pending"]})]}),d&&m&&e.jsxs(D.div,{initial:{opacity:0,y:-20},animate:{opacity:1,y:0},exit:{opacity:0,y:-20},className:j("flex items-center gap-2 px-3 py-2 rounded-lg bg-primary/10 border border-primary/20",a&&"px-2 py-1",t),children:[e.jsx(L,{className:j("text-primary animate-spin",a?"h-3 w-3":"h-4 w-4")}),!a&&e.jsx("span",{className:"text-sm text-primary font-medium",children:"Syncing..."})]})]})}function le(){const{isOnline:t,pendingCount:n}=k();return t?null:e.jsx(D.div,{initial:{height:0,opacity:0},animate:{height:"auto",opacity:1},exit:{height:0,opacity:0},className:"bg-destructive text-destructive-foreground",children:e.jsxs("div",{className:"flex items-center justify-center gap-2 py-2 px-4 text-sm",children:[e.jsx(H,{className:"h-4 w-4"}),e.jsx("span",{children:"You're offline. Changes will sync when you're back online."}),n>0&&e.jsxs(N,{variant:"secondary",className:"ml-2",children:[n," pending"]})]})})}function de({className:t}){const{isOnline:n}=k();return e.jsx("div",{className:j("flex items-center gap-1.5",t),children:n?e.jsxs(e.Fragment,{children:[e.jsx(ne,{className:"h-4 w-4 text-success"}),e.jsx("span",{className:"text-xs text-muted-foreground",children:"Connected"})]}):e.jsxs(e.Fragment,{children:[e.jsx(H,{className:"h-4 w-4 text-destructive"}),e.jsx("span",{className:"text-xs text-destructive",children:"Offline"})]})})}const ue=Object.freeze(Object.defineProperty({__proto__:null,ConnectionStatus:de,OfflineBanner:le,OfflineIndicator:ce},Symbol.toStringTag,{value:"Module"}));export{je as PWAFeatures};
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.