(()=>{class e{constructor(e={}){this.options={apiUrl:"",logToServer:!0,displayNotifications:!0,notificationDuration:5e3,retryEnabled:!0,maxRetries:3,...e},this.retryCount=0}async log(e,t={},o=null){console.error("API Error:",e,t);const r=this.getErrorType(e),n=this.getErrorMessage(e,r);switch(this.options.logToServer&&await this.logErrorToServer(r,n,t),r){case"network":case"server":if(this.options.retryEnabled&&this.retryCount<this.options.maxRetries&&o)return this.retryCount++,this.retryWithBackoff(o);break;case"auth":this.handleAuthError();break;case"rate_limit":return this.handleRateLimitError(o)}return this.options.displayNotifications&&this.displayErrorNotification(n,r,o),o&&this.options.retryEnabled||(this.retryCount=0),{success:!1,error:r,message:n,context:t}}getErrorType(e){if("AbortError"===e.name)return"timeout";if(!navigator.onLine)return"offline";if(e.response){const t=e.response.status;if(t>=400&&t<500)return 401===t||403===t?"auth":429===t?"rate_limit":"client";if(t>=500)return"server"}return"network"}getErrorMessage(e,t){const o={network:"We couldn't connect to the server. Please check your connection and try again.",timeout:"The request took too long to complete. Please try again.",offline:"You appear to be offline. Please check your internet connection.",auth:"Your session may have expired. Please log in again.",rate_limit:"You've made too many requests. Please wait a moment and try again.",server:"We're experiencing technical difficulties. Please try again later.",client:"Something went wrong with your request. Please try again.",unknown:"An unexpected error occurred. Please try again."};return e.response&&e.response.data&&e.response.data.message?e.response.data.message:e.message?e.message:o[t]||o.unknown}async logErrorToServer(e,t,o){try{if(!this.options.apiUrl)return;const r={...o,url:window.location.href,pathname:window.location.pathname,userAgent:navigator.userAgent,timestamp:(new Date).toISOString(),viewport:`${window.innerWidth}x${window.innerHeight}`,component:o.component||this.extractComponentFromStack(o.stack),method:o.method||this.extractMethodFromStack(o.stack),stack:o.stack||o.error?.stack,isLoggedIn:window.auth.isAuthenticated(),source:"frontend"},n=new FormData;n.append("error_type",e),n.append("message",t),n.append("context",JSON.stringify(r)),await fetch(`${this.options.apiUrl}errors/log`,{method:"POST",headers:{"X-WP-Nonce":window.auth.getNonce()},body:n})}catch(e){console.warn("Failed to log error to server",e)}}extractComponentFromStack(e){if(!e)return"Unknown";const t=e.match(/at\s+(\w+)\./);return t?t[1]:"Unknown"}extractMethodFromStack(e){if(!e)return null;const t=e.match(/at\s+\w+\.(\w+)\s+/);return t?t[1]:null}displayErrorNotification(e,t,o){if(window.jvbNotifications){const t=[];return o&&t.push({label:"Try Again",icon:"refresh",action:o}),void window.jvbNotifications.queuePopupNotification({type:"error",message:e,icon:"alert",priority:"high",displayDuration:this.options.notificationDuration,actions:t})}alert(e)}handleAuthError(){window.jvbSettings&&window.jvbSettings.loginUrl?window.location.href=window.jvbSettings.loginUrl:window.location.reload()}async handleRateLimitError(e){const t=2e3*(this.retryCount+1);if(await new Promise((e=>setTimeout(e,t))),e)return this.retryCount++,e()}async retryWithBackoff(e){const t=Math.min(1e3*Math.pow(2,this.retryCount),1e4);return this.options.displayNotifications&&this.displayRetryNotification(t),await new Promise((e=>setTimeout(e,t))),e()}displayRetryNotification(e){window.jvbNotifications&&window.jvbNotifications.queuePopupNotification({type:"info",message:`Retrying in ${e/1e3} seconds...`,icon:"refresh",priority:"medium",displayDuration:e})}resetRetryCount(){this.retryCount=0}collectUserFeedback(e){const t=document.createElement("dialog");return t.className="error-feedback-modal",t.innerHTML='\n <h2>Help Us Improve</h2>\n <p>We encountered an error. Would you like to tell us what happened?</p>\n <form method="dialog" data-save="error">\n <textarea placeholder="What were you trying to do when this error occurred?"></textarea>\n <div class="actions">\n <button value="cancel">Skip</button>\n <button value="submit" class="primary">Send Feedback</button>\n </div>\n </form>\n ',document.body.appendChild(t),new Promise((e=>{t.addEventListener("close",(()=>{const o="submit"===t.returnValue?t.querySelector("textarea").value:null;document.body.removeChild(t),e(o)})),t.showModal()}))}setupGlobalErrorHandling(){window.addEventListener("error",(e=>{this.log(e.error||new Error(e.message),{message:e.message,filename:e.filename,lineno:e.lineno,colno:e.colno,type:"global_error"})})),window.addEventListener("unhandledrejection",(e=>{this.log(e.reason,{type:"unhandled_promise",message:e.reason?.message||"Unhandled promise rejection"})}))}}document.addEventListener("DOMContentLoaded",(async function(){window.auth.subscribe((t=>{"auth-loaded"===t&&(window.jvbError=new e({api:jvbSettings.api,logToServer:!0,displayNotifications:!0,notificationDuration:5e3,retryEnabled:!0,maxRetries:3}))}))}))})();
|