@product7/feedback-sdk 1.0.5 → 1.0.6

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.
@@ -1,2 +1,2 @@
1
- !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).FeedbackSDK={})}(this,function(e){"use strict";class t extends Error{constructor(e,n){super(e),this.name="SDKError",this.cause=n,Error.captureStackTrace&&Error.captureStackTrace(this,t)}}class n extends Error{constructor(e,t,i){super(t),this.name="APIError",this.status=e,this.response=i,Error.captureStackTrace&&Error.captureStackTrace(this,n)}isNetworkError(){return 0===this.status}isClientError(){return this.status>=400&&this.status<500}isServerError(){return this.status>=500&&this.status<600}}class i extends Error{constructor(e,t,n){super(e),this.name="WidgetError",this.widgetType=t,this.widgetId=n,Error.captureStackTrace&&Error.captureStackTrace(this,i)}}class o extends Error{constructor(e,t){super(e),this.name="ConfigError",this.configKey=t,Error.captureStackTrace&&Error.captureStackTrace(this,o)}}class s extends Error{constructor(e,t,n){super(e),this.name="ValidationError",this.field=t,this.value=n,Error.captureStackTrace&&Error.captureStackTrace(this,s)}}class r{constructor(e={}){this.workspace=e.workspace,this.sessionToken=null,this.sessionExpiry=null,this.userContext=e.userContext||null,e.apiUrl?this.baseURL=e.apiUrl:this.workspace?this.baseURL=`https://${this.workspace}.api.staging.product7.io/api/v1`:this.baseURL="https://api.staging.product7.io/api/v1",this._loadStoredSession()}async init(e=null){if(e&&(this.userContext=e),this.isSessionValid())return{sessionToken:this.sessionToken};if(!this.userContext||!this.workspace){const e=`Missing ${this.workspace?"user context":"workspace"} for initialization`;throw new n(400,e)}const t={workspace:this.workspace,user:this.userContext};try{const e=await this._makeRequest("/widget/init",{method:"POST",body:JSON.stringify(t),headers:{"Content-Type":"application/json"}});return this.sessionToken=e.session_token,this.sessionExpiry=new Date(Date.now()+1e3*e.expires_in),this._storeSession(),{sessionToken:this.sessionToken,config:e.config||{},expiresIn:e.expires_in}}catch(e){throw new n(e.status||500,`Failed to initialize widget: ${e.message}`,e.response)}}async submitFeedback(e){if(this.isSessionValid()||await this.init(),!this.sessionToken)throw new n(401,"No valid session token available");const t={board:e.board_id||e.board||e.boardId,title:e.title,content:e.content,attachments:e.attachments||[]};try{return await this._makeRequest("/widget/feedback",{method:"POST",body:JSON.stringify(t),headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.sessionToken}`}})}catch(t){if(401===t.status)return this.sessionToken=null,this.sessionExpiry=null,await this.init(),this.submitFeedback(e);throw new n(t.status||500,`Failed to submit feedback: ${t.message}`,t.response)}}isSessionValid(){return this.sessionToken&&this.sessionExpiry&&new Date<this.sessionExpiry}setUserContext(e){this.userContext=e,"undefined"!=typeof localStorage&&localStorage.setItem("feedbackSDK_userContext",JSON.stringify(e))}getUserContext(){return this.userContext}clearSession(){this.sessionToken=null,this.sessionExpiry=null,"undefined"!=typeof localStorage&&(localStorage.removeItem("feedbackSDK_session"),localStorage.removeItem("feedbackSDK_userContext"))}_storeSession(){if("undefined"!=typeof localStorage)try{const e={token:this.sessionToken,expiry:this.sessionExpiry.toISOString(),workspace:this.workspace};localStorage.setItem("feedbackSDK_session",JSON.stringify(e))}catch(e){}}_loadStoredSession(){if("undefined"==typeof localStorage)return!1;try{const e=localStorage.getItem("feedbackSDK_session");if(!e)return!1;const t=JSON.parse(e);return this.sessionToken=t.token,this.sessionExpiry=new Date(t.expiry),this.isSessionValid()}catch(e){return!1}}async _makeRequest(e,t={}){const i=`${this.baseURL}${e}`;try{const e=await fetch(i,t);if(!e.ok){let t=`HTTP ${e.status}`,i=null;try{i=await e.json(),t=i.message||i.error||t}catch(n){t=await e.text()||t}throw new n(e.status,t,i)}const o=e.headers.get("content-type");return o&&o.includes("application/json")?await e.json():await e.text()}catch(e){if(e instanceof n)throw e;throw new n(0,e.message,null)}}}class a{constructor(){this.events=new Map}on(e,t){return this.events.has(e)||this.events.set(e,[]),this.events.get(e).push(t),()=>this.off(e,t)}off(e,t){const n=this.events.get(e);if(n){const e=n.indexOf(t);e>-1&&n.splice(e,1)}}emit(e,t){const n=this.events.get(e);n&&n.forEach(e=>{try{e(t)}catch(e){console.error("[FeedbackSDK] Event callback error:",e)}})}once(e,t){const n=this.on(e,e=>{t(e),n()});return n}clear(){this.events.clear()}getListenerCount(e){const t=this.events.get(e);return t?t.length:0}}function d(e="feedback"){return`${e}_${Date.now()}_${Math.random().toString(36).substring(2,9)}`}function c(e,t){const n={...e};for(const i in t)t.hasOwnProperty(i)&&(t[i]&&"object"==typeof t[i]&&!Array.isArray(t[i])?n[i]=c(e[i]||{},t[i]):n[i]=t[i]);return n}function l(){return"undefined"!=typeof window&&"undefined"!=typeof document}var u=Object.freeze({__proto__:null,debounce:function(e,t){let n;return function(...i){clearTimeout(n),n=setTimeout(()=>{clearTimeout(n),e(...i)},t)}},deepMerge:c,delay:function(e){return new Promise(t=>setTimeout(t,e))},escapeRegex:function(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")},formatFileSize:function(e){if(0===e)return"0 Bytes";const t=Math.floor(Math.log(e)/Math.log(1024));return parseFloat((e/Math.pow(1024,t)).toFixed(2))+" "+["Bytes","KB","MB","GB"][t]},generateId:d,getBrowserInfo:function(){return{userAgent:navigator.userAgent,platform:navigator.platform,language:navigator.language||navigator.userLanguage,cookieEnabled:navigator.cookieEnabled,screenResolution:`${screen.width}x${screen.height}`,windowSize:`${window.innerWidth}x${window.innerHeight}`,timezone:Intl.DateTimeFormat().resolvedOptions().timeZone}},getCSSProperty:function(e,t,n=""){if(!e||!t)return n;try{return window.getComputedStyle(e).getPropertyValue(t)||n}catch(e){return n}},getCurrentTimestamp:function(){return(new Date).toISOString()},getNestedProperty:function(e,t,n=void 0){if(!e||!t)return n;const i=t.split(".");let o=e;for(const e of i){if(null==o||!(e in o))return n;o=o[e]}return o},isBrowser:l,isInViewport:function(e){if(!e)return!1;const t=e.getBoundingClientRect();return t.top>=0&&t.left>=0&&t.bottom<=(window.innerHeight||document.documentElement.clientHeight)&&t.right<=(window.innerWidth||document.documentElement.clientWidth)},isMobile:function(){return!!l()&&(/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)||window.innerWidth<=768)},isValidEmail:function(e){return!(!e||"string"!=typeof e)&&/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e.trim())},safeJsonParse:function(e,t=null){try{return JSON.parse(e)}catch(e){return t}},sanitizeHTML:function(e){if(!e||"string"!=typeof e)return"";const t=document.createElement("div");return t.textContent=e,t.innerHTML},scrollToElement:function(e,t={}){if(!e)return;e.scrollIntoView({behavior:"smooth",block:"center",inline:"nearest",...t})},setNestedProperty:function(e,t,n){if(!e||!t)return e;const i=t.split("."),o=i.pop();let s=e;for(const e of i)e in s&&"object"==typeof s[e]||(s[e]={}),s=s[e];return s[o]=n,e},throttle:function(e,t){let n,i;return function(...o){i?(clearTimeout(n),n=setTimeout(()=>{Date.now()-i>=t&&(e(...o),i=Date.now())},t-(Date.now()-i))):(e(...o),i=Date.now())}},validateConfig:function(e,t=[]){const n=[];for(const i of t)e[i]||n.push(i);if(n.length>0)throw new Error(`Missing required configuration: ${n.join(", ")}`);return!0}});class h{constructor(e={}){this.id=e.id,this.sdk=e.sdk,this.apiService=e.apiService,this.type=e.type||"base",this.options={container:null,position:this.sdk.config.position,theme:this.sdk.config.theme,boardId:this.sdk.config.boardId,autoShow:!1,customStyles:{},...e},this.element=null,this.modalElement=null,this.mounted=!1,this.destroyed=!1,this.state={isOpen:!1,isSubmitting:!1,title:"",content:"",email:"",attachments:[],errors:{}},this._bindMethods()}mount(e){return this.mounted||this.destroyed||("string"==typeof e&&(e=document.querySelector(e)),e||(e=document.body),this.container=e,this.element=this._render(),this.container.appendChild(this.element),this.mounted=!0,this._attachEvents(),this.onMount(),this.options.autoShow&&this.show(),this.sdk.eventBus.emit("widget:mounted",{widget:this})),this}show(){return this.element&&(this.element.style.display="block"),this}hide(){return this.element&&(this.element.style.display="none"),this}openModal(){this.state.isOpen=!0,this._renderModal()}closeModal(){this.state.isOpen=!1,this.modalElement&&(this.modalElement.remove(),this.modalElement=null),this._resetForm()}async submitFeedback(){if(!this.state.isSubmitting)try{this.state.isSubmitting=!0,this._updateSubmitButton();const e={title:this.state.title||"Feedback",content:this.state.content,email:this.state.email,board_id:this.options.boardId,attachments:this.state.attachments};if(!this.state.content.trim())return void this._showError("Please enter your feedback message.");const t=await this.apiService.submitFeedback(e);this._showSuccessMessage(),this.closeModal(),this.sdk.eventBus.emit("feedback:submitted",{widget:this,feedback:t})}catch(e){this._showError("Failed to submit feedback. Please try again."),this.sdk.eventBus.emit("feedback:error",{widget:this,error:e})}finally{this.state.isSubmitting=!1,this._updateSubmitButton()}}handleConfigUpdate(e){this.options.theme=e.theme,this.element&&this._updateTheme()}destroy(){this.destroyed||(this.onDestroy(),this.closeModal(),this.element&&this.element.parentNode&&this.element.parentNode.removeChild(this.element),this.destroyed=!0,this.mounted=!1,this.sdk.eventBus.emit("widget:destroyed",{widget:this}))}onMount(){}onDestroy(){}_render(){throw new Error("_render() must be implemented by concrete widget")}_attachEvents(){}_bindMethods(){this.openModal=this.openModal.bind(this),this.closeModal=this.closeModal.bind(this),this.submitFeedback=this.submitFeedback.bind(this)}_renderModal(){if(this.modalElement)return;this.modalElement=document.createElement("div"),this.modalElement.className=`feedback-modal theme-${this.options.theme}`,this.modalElement.innerHTML=this._getModalHTML(),document.body.appendChild(this.modalElement),this._attachModalEvents();const e=this.modalElement.querySelector("input, textarea");e&&setTimeout(()=>e.focus(),100)}_getModalHTML(){return`\n <div class="feedback-modal-overlay">\n <div class="feedback-modal-content">\n <div class="feedback-modal-header">\n <h3>Send Feedback</h3>\n <button class="feedback-modal-close" type="button">&times;</button>\n </div>\n <form class="feedback-form">\n <div class="feedback-form-group">\n <label for="feedback-title-${this.id}">Title</label>\n <input \n type="text" \n id="feedback-title-${this.id}" \n name="title" \n placeholder="Brief description of your feedback"\n value="${this.state.title}"\n />\n </div>\n <div class="feedback-form-group">\n <label for="feedback-content-${this.id}">Message *</label>\n <textarea \n id="feedback-content-${this.id}" \n name="content" \n placeholder="Tell us more about your feedback..."\n required\n >${this.state.content}</textarea>\n </div>\n <div class="feedback-form-actions">\n <button type="button" class="feedback-btn feedback-btn-cancel">Cancel</button>\n <button type="submit" class="feedback-btn feedback-btn-submit">\n ${this.state.isSubmitting?"Sending...":"Send Feedback"}\n </button>\n </div>\n <div class="feedback-error" style="display: none;"></div>\n </form>\n </div>\n </div>\n `}_attachModalEvents(){const e=this.modalElement;e.querySelector(".feedback-modal-close").addEventListener("click",this.closeModal),e.querySelector(".feedback-btn-cancel").addEventListener("click",this.closeModal),e.querySelector(".feedback-modal-overlay").addEventListener("click",e=>{e.target===e.currentTarget&&this.closeModal()});e.querySelector(".feedback-form").addEventListener("submit",e=>{e.preventDefault(),this.submitFeedback()}),e.querySelector('input[name="title"]').addEventListener("input",e=>{this.state.title=e.target.value}),e.querySelector('textarea[name="content"]').addEventListener("input",e=>{this.state.content=e.target.value})}_updateSubmitButton(){if(this.modalElement){const e=this.modalElement.querySelector(".feedback-btn-submit");e&&(e.textContent=this.state.isSubmitting?"Sending...":"Send Feedback",e.disabled=this.state.isSubmitting)}}_showError(e){if(this.modalElement){const t=this.modalElement.querySelector(".feedback-error");t.textContent=e,t.style.display="block",setTimeout(()=>{t&&(t.style.display="none")},5e3)}}_showSuccessMessage(){const e=document.createElement("div");e.className="feedback-success-notification",e.innerHTML='\n <div class="feedback-success-content">\n <span>✓ Feedback submitted successfully!</span>\n <button class="feedback-success-close">&times;</button>\n </div>\n ',document.body.appendChild(e),setTimeout(()=>{e.parentNode&&e.parentNode.removeChild(e)},3e3),e.querySelector(".feedback-success-close").addEventListener("click",()=>{e.parentNode&&e.parentNode.removeChild(e)})}_resetForm(){this.state.title="",this.state.content="",this.state.email="",this.state.errors={}}_updateTheme(){this.element&&(this.element.className=this.element.className.replace(/theme-\w+/,`theme-${this.options.theme}`)),this.modalElement&&(this.modalElement.className=this.modalElement.className.replace(/theme-\w+/,`theme-${this.options.theme}`))}}class f extends h{constructor(e){super({...e,type:"button"})}_render(){const e=document.createElement("div");return e.className=`feedback-widget feedback-widget-button theme-${this.options.theme} position-${this.options.position}`,e.innerHTML='\n <button class="feedback-trigger-btn" type="button">\n <svg width="20" height="20" viewBox="0 0 20 20" fill="currentColor">\n <path d="M2.003 5.884L10 9.882l7.997-3.998A2 2 0 0016 4H4a2 2 0 00-1.997 1.884z"/>\n <path d="M18 8.118l-8 4-8-4V14a2 2 0 002 2h12a2 2 0 002-2V8.118z"/>\n </svg>\n Feedback\n </button>\n ',this.options.customStyles&&Object.assign(e.style,this.options.customStyles),e}_attachEvents(){const e=this.element.querySelector(".feedback-trigger-btn");e.addEventListener("click",this.openModal),e.addEventListener("mouseenter",()=>{this.state.isSubmitting||(e.style.transform="translateY(-2px)")}),e.addEventListener("mouseleave",()=>{e.style.transform="translateY(0)"})}updateText(e){const t=this.element?.querySelector(".feedback-trigger-btn");if(t){const n=t.childNodes[t.childNodes.length-1];n&&n.nodeType===Node.TEXT_NODE&&(n.textContent=e)}}updatePosition(e){this.options.position=e,this.element&&(this.element.className=this.element.className.replace(/position-\w+-\w+/,`position-${e}`))}}class p extends h{constructor(e){super({...e,type:"inline"})}_render(){const e=document.createElement("div");return e.className=`feedback-widget feedback-widget-inline theme-${this.options.theme}`,e.innerHTML=`\n <div class="feedback-inline-content">\n <h3>Send us your feedback</h3>\n <form class="feedback-inline-form">\n <div class="feedback-form-group">\n <input \n type="text" \n name="title" \n placeholder="Title (optional)"\n value="${this.state.title}"\n />\n </div>\n <div class="feedback-form-group">\n <textarea \n name="content" \n placeholder="Your feedback..."\n required\n >${this.state.content}</textarea>\n </div>\n <div class="feedback-form-group">\n <input \n type="email" \n name="email" \n placeholder="Email (optional)"\n value="${this.state.email}"\n />\n </div>\n <button type="submit" class="feedback-btn feedback-btn-submit">\n Send Feedback\n </button>\n <div class="feedback-error" style="display: none;"></div>\n </form>\n </div>\n `,this.options.customStyles&&Object.assign(e.style,this.options.customStyles),e}_attachEvents(){const e=this.element.querySelector(".feedback-inline-form");e.addEventListener("submit",e=>{e.preventDefault(),this.submitFeedback()}),e.querySelector('input[name="title"]').addEventListener("input",e=>{this.state.title=e.target.value}),e.querySelector('textarea[name="content"]').addEventListener("input",e=>{this.state.content=e.target.value}),e.querySelector('input[name="email"]').addEventListener("input",e=>{this.state.email=e.target.value})}openModal(){const e=this.element.querySelector('textarea[name="content"]');e&&e.focus()}closeModal(){}_showSuccessMessage(){const e=this.element.querySelector(".feedback-inline-content"),t=e.innerHTML;e.innerHTML='\n <div class="feedback-success">\n <div class="feedback-success-icon">✓</div>\n <h3>Thank you!</h3>\n <p>Your feedback has been submitted successfully.</p>\n <button class="feedback-btn feedback-btn-reset">Send Another</button>\n </div>\n ';e.querySelector(".feedback-btn-reset").addEventListener("click",()=>{e.innerHTML=t,this._attachEvents(),this._resetForm()})}_showError(e){const t=this.element.querySelector(".feedback-error");t&&(t.textContent=e,t.style.display="block",setTimeout(()=>{t&&(t.style.display="none")},5e3))}_updateSubmitButton(){const e=this.element.querySelector(".feedback-btn-submit");e&&(e.textContent=this.state.isSubmitting?"Sending...":"Send Feedback",e.disabled=this.state.isSubmitting)}updateTitle(e){const t=this.element?.querySelector("h3");t&&(t.textContent=e)}setPlaceholder(e,t){const n=this.element?.querySelector(`[name="${e}"]`);n&&(n.placeholder=t)}}class m extends h{constructor(e){super({...e,type:"tab"})}_render(){const e=document.createElement("div");return e.className=`feedback-widget feedback-widget-tab theme-${this.options.theme} position-${this.options.position}`,e.innerHTML='\n <div class="feedback-tab-trigger">\n <span class="feedback-tab-text">Feedback</span>\n </div>\n ',this.options.customStyles&&Object.assign(e.style,this.options.customStyles),e}_attachEvents(){const e=this.element.querySelector(".feedback-tab-trigger");e.addEventListener("click",this.openModal),e.addEventListener("mouseenter",()=>{this.state.isSubmitting||(e.style.transform=this._getHoverTransform())}),e.addEventListener("mouseleave",()=>{e.style.transform="none"})}_getHoverTransform(){const e=this.options.position;return e.includes("right")?"translateX(-5px)":e.includes("left")?"translateX(5px)":"none"}updateText(e){const t=this.element?.querySelector(".feedback-tab-text");t&&(t.textContent=e)}updatePosition(e){this.options.position=e,this.element&&(this.element.className=this.element.className.replace(/position-\w+-\w+/,`position-${e}`))}}class b{static widgets=new Map([["button",f],["tab",m],["inline",p]]);static register(e,n){if("string"!=typeof e||!e.trim())throw new t("Widget type must be a non-empty string");if("function"!=typeof n)throw new t("Widget class must be a constructor function");this.widgets.set(e,n)}static create(e,n={}){const i=this.widgets.get(e);if(!i){const n=Array.from(this.widgets.keys()).join(", ");throw new t(`Unknown widget type: ${e}. Available types: ${n}`)}try{return new i(n)}catch(n){throw new t(`Failed to create widget of type '${e}': ${n.message}`,n)}}static getAvailableTypes(){return Array.from(this.widgets.keys())}static isTypeRegistered(e){return this.widgets.has(e)}static unregister(e){return this.widgets.delete(e)}static clear(){this.widgets.clear()}static getWidgetClass(e){return this.widgets.get(e)}}class g{constructor(e={}){this.config=this._validateAndMergeConfig(e),this.initialized=!1,this.widgets=new Map,this.eventBus=new a,this.apiService=new r({apiUrl:this.config.apiUrl,workspace:this.config.workspace,userContext:this.config.userContext}),this._bindMethods()}async init(){if(this.initialized)return{alreadyInitialized:!0};try{const e=await this.apiService.init(this.config.userContext);return e.config&&(this.config=c(this.config,e.config)),this.initialized=!0,this.eventBus.emit("sdk:initialized",{config:this.config,sessionToken:e.sessionToken}),{initialized:!0,config:e.config||{},sessionToken:e.sessionToken,expiresIn:e.expiresIn}}catch(e){throw this.eventBus.emit("sdk:error",{error:e}),new t(`Failed to initialize SDK: ${e.message}`,e)}}createWidget(e="button",n={}){if(!this.initialized)throw new t("SDK must be initialized before creating widgets. Call init() first.");const i=d("widget"),o={id:i,sdk:this,apiService:this.apiService,...this.config,...n};try{const t=b.create(e,o);return this.widgets.set(i,t),this.eventBus.emit("widget:created",{widget:t,type:e}),t}catch(e){throw new t(`Failed to create widget: ${e.message}`,e)}}getWidget(e){return this.widgets.get(e)}getAllWidgets(){return Array.from(this.widgets.values())}destroyWidget(e){const t=this.widgets.get(e);return!!t&&(t.destroy(),this.widgets.delete(e),this.eventBus.emit("widget:removed",{widgetId:e}),!0)}destroyAllWidgets(){for(const e of this.widgets.values())e.destroy();this.widgets.clear(),this.eventBus.emit("widgets:cleared")}updateConfig(e){const t={...this.config};this.config=this._validateAndMergeConfig(e,this.config);for(const e of this.widgets.values())e.handleConfigUpdate(this.config);this.eventBus.emit("config:updated",{oldConfig:t,newConfig:this.config})}setUserContext(e){this.config.userContext=e,this.apiService&&this.apiService.setUserContext(e),this.eventBus.emit("user:updated",{userContext:e})}getUserContext(){return this.config.userContext||(this.apiService?this.apiService.getUserContext():null)}async reinitialize(e=null){return this.apiService.clearSession(),this.initialized=!1,e&&this.setUserContext(e),this.init()}on(e,t){return this.eventBus.on(e,t),this}off(e,t){return this.eventBus.off(e,t),this}once(e,t){return this.eventBus.once(e,t),this}emit(e,t){return this.eventBus.emit(e,t),this}destroy(){this.destroyAllWidgets(),this.eventBus.removeAllListeners(),this.apiService.clearSession(),this.initialized=!1,this.eventBus.emit("sdk:destroyed")}_validateAndMergeConfig(e,t={}){const n=c(c({apiUrl:null,workspace:null,userContext:null,position:"bottom-right",theme:"light",boardId:"general",autoShow:!0,debug:!1},t),e);if(!n.workspace)throw new o("Missing required configuration: workspace");return n.userContext&&this._validateUserContext(n.userContext),n}_validateUserContext(e){if(!e.user_id&&!e.email)throw new o("User context must include at least user_id or email");const t={user_id:"string",email:"string",name:"string",custom_fields:"object",company:"object"};for(const[n,i]of Object.entries(t))if(e[n]&&typeof e[n]!==i)throw new o(`User context field '${n}' must be of type '${i}'`)}_bindMethods(){this.createWidget=this.createWidget.bind(this),this.destroyWidget=this.destroyWidget.bind(this),this.updateConfig=this.updateConfig.bind(this)}static create(e){return new g(e)}static async createAndInit(e){const t=new g(e);return await t.init(),t}static extractUserContextFromAuth(e){return e?{user_id:e.sub||e.id||e.user_id,email:e.email,name:e.name||e.display_name||e.full_name,custom_fields:{role:e.role,plan:e.plan||e.subscription?.plan,...e.custom_fields||{}},company:e.company||e.organization?{id:e.company?.id||e.organization?.id,name:e.company?.name||e.organization?.name,monthly_spend:e.company?.monthly_spend}:void 0}:null}}function k(){if("undefined"!=typeof document&&!document.querySelector("#feedback-sdk-styles")){const e=document.createElement("style");e.id="feedback-sdk-styles",e.textContent="\n.feedback-widget {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', Oxygen, Ubuntu, Cantarell, sans-serif;\n font-size: 14px;\n line-height: 1.4;\n z-index: 999999;\n box-sizing: border-box;\n}\n\n.feedback-widget *,\n.feedback-widget *::before,\n.feedback-widget *::after {\n box-sizing: border-box;\n}\n\n.feedback-widget-button {\n position: fixed;\n z-index: 999999;\n}\n\n.feedback-widget-button.position-bottom-right {\n bottom: 20px;\n right: 20px;\n}\n\n.feedback-widget-button.position-bottom-left {\n bottom: 20px;\n left: 20px;\n}\n\n.feedback-widget-button.position-top-right {\n top: 20px;\n right: 20px;\n}\n\n.feedback-widget-button.position-top-left {\n top: 20px;\n left: 20px;\n}\n\n.feedback-trigger-btn {\n position: relative;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 12px;\n height: 44px;\n overflow: hidden;\n border-radius: 0.5rem;\n border: none;\n padding: 10px 16px;\n font-size: 14px;\n font-weight: 500;\n font-family: inherit;\n cursor: pointer;\n transition: all 0.3s duration;\n color: white;\n background: #155EEF;\n box-shadow: 0 1px 2px 0 rgba(16, 24, 40, 0.05);\n}\n\n.feedback-trigger-btn:hover:not(:disabled) {\n background: #004EEB;\n box-shadow: 0 1px 2px 0 rgba(16, 24, 40, 0.1);\n}\n\n.feedback-trigger-btn:disabled {\n opacity: 0.7;\n cursor: not-allowed;\n}\n\n.feedback-trigger-btn:focus-visible {\n outline: 2px solid #155EEF;\n outline-offset: 2px;\n}\n\n.feedback-modal {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n z-index: 1000000;\n display: flex;\n align-items: center;\n justify-content: center;\n font-family: inherit;\n}\n\n.feedback-modal-overlay {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.feedback-modal-content {\n background: white;\n border-radius: 8px;\n box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);\n min-width: 460px;\n max-width: 500px;\n width: 100%;\n padding: 16px;\n max-height: 85vh;\n overflow-y: hidden;\n position: relative;\n}\n\n.feedback-modal.theme-dark .feedback-modal-content {\n background: #1F2937;\n color: white;\n}\n\n.feedback-modal-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 16px;\n border-bottom: 1px solid #D1D5DB;\n flex-shrink: 0;\n}\n\n.feedback-modal.theme-dark .feedback-modal-header {\n border-bottom-color: #374151;\n}\n\n.feedback-modal-header h3 {\n margin: 0;\n font-size: 16px;\n font-weight: 600;\n}\n\n.feedback-modal-close {\n background: none;\n border: none;\n font-size: 24px;\n cursor: pointer;\n color: #6B7280;\n padding: 0;\n width: 24px;\n height: 24px;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.3s ease;\n}\n\n.feedback-modal-close:hover {\n color: #374151;\n}\n\n.feedback-modal-close:focus-visible {\n outline: 2px solid #155EEF;\n outline-offset: 2px;\n}\n\n.feedback-modal.theme-dark .feedback-modal-close {\n color: #9CA3AF;\n}\n\n.feedback-modal.theme-dark .feedback-modal-close:hover {\n color: #D1D5DB;\n}\n\n.feedback-form {\n padding: 16px;\n}\n\n.feedback-form-group {\n display: flex;\n flex-direction: column;\n gap: 4px;\n margin-bottom: 12px;\n}\n\n.feedback-form-group:last-child {\n margin-bottom: 0;\n}\n\n.feedback-form-group label {\n font-size: 14px;\n font-weight: 500;\n line-height: 1.25;\n color: #374151;\n}\n\n.feedback-modal.theme-dark .feedback-form-group label {\n color: #D1D5DB;\n}\n\n.feedback-form-group input {\n height: 40px;\n width: 100%;\n border-radius: 6px;\n border: 1px solid #D1D5DB;\n padding: 2px 12px;\n font-size: 14px;\n font-weight: 400;\n line-height: 1.25;\n color: #1F2937;\n font-family: inherit;\n outline: none;\n transition: all 0.2s ease;\n}\n\n.feedback-form-group input::placeholder {\n font-size: 14px;\n color: #6B7280;\n}\n\n.feedback-form-group input:focus {\n border-color: #84ADFF;\n box-shadow: 0 0 0 1px rgba(16, 24, 40, 0.05), 0 0 0 3px rgba(41, 112, 255, 0.2);\n}\n\n.feedback-form-group input:focus-visible {\n outline: none;\n}\n\n.feedback-form-group textarea {\n min-height: 100px;\n width: 100%;\n resize: both;\n border-radius: 6px;\n border: 1px solid #D1D5DB;\n padding: 2px 12px;\n font-size: 14px;\n font-weight: 400;\n line-height: 1.25;\n color: #1F2937;\n font-family: inherit;\n outline: none;\n transition: all 0.2s ease;\n}\n\n.feedback-form-group textarea::placeholder {\n font-size: 14px;\n color: #6B7280;\n}\n\n.feedback-form-group textarea:focus {\n border-color: #84ADFF;\n box-shadow: 0 0 0 1px rgba(16, 24, 40, 0.05), 0 0 0 3px rgba(41, 112, 255, 0.2);\n}\n\n.feedback-form-group textarea:focus-visible {\n outline: none;\n}\n\n.feedback-modal.theme-dark .feedback-form-group input,\n.feedback-modal.theme-dark .feedback-form-group textarea {\n background: #374151;\n border-color: #4B5563;\n color: white;\n}\n\n.feedback-btn {\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n overflow: hidden;\n border-radius: 6px;\n border: none;\n height: 40px;\n padding: 2px 16px;\n font-size: 14px;\n font-weight: 500;\n font-family: inherit;\n cursor: pointer;\n transition: all 0.2s ease;\n}\n\n.feedback-btn:disabled {\n opacity: 0.7;\n cursor: not-allowed;\n}\n\n.feedback-btn:focus-visible {\n outline: 2px solid #155EEF;\n outline-offset: 2px;\n}\n\n.feedback-btn-submit {\n background: #155EEF;\n color: white;\n}\n\n.feedback-btn-submit:hover:not(:disabled) {\n background: #004EEB;\n}\n\n.feedback-btn-cancel {\n background: transparent;\n color: #6B7280;\n border: 1px solid #D1D5DB;\n}\n\n.feedback-btn-cancel:hover:not(:disabled) {\n background: #F9FAFB;\n border-color: #9CA3AF;\n color: #374151;\n}\n\n.feedback-modal.theme-dark .feedback-btn-cancel {\n color: #D1D5DB;\n border-color: #4B5563;\n}\n\n.feedback-modal.theme-dark .feedback-btn-cancel:hover:not(:disabled) {\n background: #374151;\n}\n\n.feedback-form-actions {\n display: flex;\n gap: 8px;\n justify-content: flex-end;\n margin-top: 16px;\n padding-top: 4px;\n}\n\n.feedback-loading {\n width: 20px;\n height: 20px;\n border-radius: 50%;\n mask: radial-gradient(transparent 62%, white 65%);\n -webkit-mask: radial-gradient(transparent 62%, white 65%);\n animation: feedbackRotate 0.7s linear infinite;\n}\n\n.feedback-loading-white {\n background: conic-gradient(from 0deg, rgba(255, 255, 255, 0.5), white);\n}\n\n.feedback-loading-blue {\n background: conic-gradient(from 0deg, #004EEB, #eff4ff);\n}\n\n@keyframes feedbackRotate {\n 0% { transform: rotate(0deg); }\n 100% { transform: rotate(360deg); }\n}\n\n.feedback-error {\n color: #F04438;\n font-size: 14px;\n font-weight: 400;\n margin-top: 4px;\n text-transform: capitalize;\n}\n\n.feedback-form-error {\n color: #F04438;\n font-size: 14px;\n margin-top: 12px;\n padding: 8px 12px;\n background: #FEE2E2;\n border: 1px solid #FECACA;\n border-radius: 6px;\n}\n\n.feedback-modal.theme-dark .feedback-form-error {\n background: #7F1D1D;\n border-color: #991B1B;\n color: #FCA5A5;\n}\n\n.feedback-form-group.error input,\n.feedback-form-group.error textarea {\n border-color: #FDA29B;\n}\n\n.feedback-form-group.error input:focus,\n.feedback-form-group.error textarea:focus {\n border-color: #FDA29B;\n box-shadow: 0 0 0 1px rgba(16, 24, 40, 0.05), 0 0 0 4px rgba(253, 162, 155, 0.3);\n}\n\n.feedback-success-notification {\n position: fixed;\n top: 20px;\n right: 20px;\n z-index: 1000001;\n background: white;\n border: 1px solid #D1FAE5;\n border-radius: 8px;\n box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);\n animation: slideInRight 0.3s ease-out;\n}\n\n.feedback-success-content {\n display: flex;\n align-items: center;\n padding: 12px 16px;\n gap: 12px;\n}\n\n.feedback-success-content span {\n color: #059669;\n font-weight: 500;\n font-size: 14px;\n}\n\n.feedback-success-close {\n background: none;\n border: none;\n color: #6B7280;\n cursor: pointer;\n font-size: 18px;\n padding: 0;\n width: 20px;\n height: 20px;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.3s ease;\n}\n\n.feedback-success-close:hover {\n color: #374151;\n}\n\n.feedback-success-close:focus-visible {\n outline: 2px solid #155EEF;\n outline-offset: 2px;\n}\n\n@keyframes slideInRight {\n from {\n transform: translateX(100%);\n opacity: 0;\n }\n to {\n transform: translateX(0);\n opacity: 1;\n }\n}\n\n@keyframes fadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n.feedback-modal {\n animation: fadeIn 0.2s ease-out;\n}\n\n.feedback-modal-content {\n animation: slideInUp 0.3s ease-out;\n}\n\n@keyframes slideInUp {\n from {\n transform: translateY(20px);\n opacity: 0;\n }\n to {\n transform: translateY(0);\n opacity: 1;\n }\n}\n\n@media (max-width: 640px) {\n .feedback-modal {\n padding: 8px;\n }\n \n .feedback-modal-content {\n min-width: 280px;\n max-width: 100%;\n max-height: 95vh;\n }\n \n .feedback-form {\n padding: 16px;\n }\n \n .feedback-modal-header {\n padding: 16px;\n }\n \n .feedback-modal-header h3 {\n font-size: 15px;\n }\n \n .feedback-form-actions {\n flex-direction: column;\n gap: 8px;\n }\n \n .feedback-btn {\n width: 100%;\n height: 40px;\n }\n \n .feedback-widget-button {\n bottom: 16px;\n right: 16px;\n }\n \n .feedback-widget-button.position-bottom-left {\n left: 16px;\n }\n \n .feedback-trigger-btn {\n padding: 10px 16px;\n font-size: 13px;\n }\n \n .feedback-success-notification {\n top: 8px;\n right: 8px;\n left: 8px;\n max-width: none;\n }\n\n .feedback-form-group input {\n height: 40px;\n padding: 2px 12px;\n }\n \n .feedback-form-group textarea {\n min-height: 80px;\n padding: 2px 12px;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .feedback-trigger-btn,\n .feedback-btn,\n .feedback-modal,\n .feedback-modal-content,\n .feedback-success-notification,\n .feedback-loading {\n transition: none;\n animation: none;\n }\n \n .feedback-trigger-btn:hover {\n transform: none;\n }\n}\n\n@media print {\n .feedback-widget,\n .feedback-modal,\n .feedback-success-notification {\n display: none !important;\n }\n}\n",document.head.appendChild(e)}}function w(){if("undefined"!=typeof window&&window.FeedbackSDKConfig){k();const e={...window.FeedbackSDKConfig},t=new g(e);t.init().then(n=>{if(window.FeedbackSDK.instance=t,window.FeedbackSDKConfig.autoCreate){(Array.isArray(window.FeedbackSDKConfig.autoCreate)?window.FeedbackSDKConfig.autoCreate:[window.FeedbackSDKConfig.autoCreate]).forEach(e=>{try{t.createWidget(e.type||"button",e).mount(e.container)}catch(e){console.error("[FeedbackSDK] Failed to create widget:",e)}})}if("undefined"!=typeof CustomEvent){const i=new CustomEvent("FeedbackSDKReady",{detail:{sdk:t,config:e,initData:n}});window.dispatchEvent(i)}}).catch(t=>{if(console.error("[FeedbackSDK] Auto-initialization failed:",t),"undefined"!=typeof CustomEvent){const n=new CustomEvent("FeedbackSDKError",{detail:{error:t,config:e,phase:"initialization"}});window.dispatchEvent(n)}})}}const x={FeedbackSDK:g,BaseWidget:h,ButtonWidget:f,TabWidget:m,InlineWidget:p,WidgetFactory:b,EventBus:a,APIService:r,SDKError:t,APIError:n,WidgetError:i,ConfigError:o,ValidationError:s,helpers:u,create:e=>(k(),new g(e)),version:"1.0.0",instance:null,isReady:()=>Boolean(x.instance),getInstance:()=>x.instance,setUserContext:e=>{x.instance?x.instance.setUserContext(e):"undefined"!=typeof window&&(window.FeedbackSDKUserContext=e)},initWithUser:async(e,t)=>{k();const n={...e,userContext:t},i=new g(n);return await i.init(),"undefined"!=typeof window&&(window.FeedbackSDK.instance=i),i},onReady:e=>{"undefined"!=typeof window&&(x.isReady()?e(x.instance):window.addEventListener("FeedbackSDKReady",t=>{e(t.detail.sdk,t.detail)},{once:!0}))},onError:e=>{"undefined"!=typeof window&&window.addEventListener("FeedbackSDKError",t=>{e(t.detail.error,t.detail)})},extractUserContext:g.extractUserContextFromAuth};"undefined"!=typeof window&&(window.FeedbackSDK=x,"undefined"!=typeof document&&("loading"===document.readyState?document.addEventListener("DOMContentLoaded",w):setTimeout(w,0))),e.APIError=n,e.APIService=r,e.BaseWidget=h,e.ButtonWidget=f,e.ConfigError=o,e.EventBus=a,e.FeedbackSDK=g,e.InlineWidget=p,e.SDKError=t,e.TabWidget=m,e.ValidationError=s,e.WidgetError=i,e.WidgetFactory=b,e.default=x,e.helpers=u,Object.defineProperty(e,"__esModule",{value:!0})});
1
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).FeedbackSDK={})}(this,function(e){"use strict";class t extends Error{constructor(e,n){super(e),this.name="SDKError",this.cause=n,Error.captureStackTrace&&Error.captureStackTrace(this,t)}}class n extends Error{constructor(e,t,i){super(t),this.name="APIError",this.status=e,this.response=i,Error.captureStackTrace&&Error.captureStackTrace(this,n)}isNetworkError(){return 0===this.status}isClientError(){return this.status>=400&&this.status<500}isServerError(){return this.status>=500&&this.status<600}}class i extends Error{constructor(e,t,n){super(e),this.name="WidgetError",this.widgetType=t,this.widgetId=n,Error.captureStackTrace&&Error.captureStackTrace(this,i)}}class s extends Error{constructor(e,t){super(e),this.name="ConfigError",this.configKey=t,Error.captureStackTrace&&Error.captureStackTrace(this,s)}}class o extends Error{constructor(e,t,n){super(e),this.name="ValidationError",this.field=t,this.value=n,Error.captureStackTrace&&Error.captureStackTrace(this,o)}}class a{constructor(e={}){this.workspace=e.workspace,this.sessionToken=null,this.sessionExpiry=null,this.userContext=e.userContext||null,e.apiUrl?this.baseURL=e.apiUrl:this.workspace?this.baseURL=`https://${this.workspace}.api.staging.product7.io/api/v1`:this.baseURL="https://api.staging.product7.io/api/v1",this._loadStoredSession()}async init(e=null){if(e&&(this.userContext=e),this.isSessionValid())return{sessionToken:this.sessionToken};if(!this.userContext||!this.workspace){const e=`Missing ${this.workspace?"user context":"workspace"} for initialization`;throw new n(400,e)}const t={workspace:this.workspace,user:this.userContext};try{const e=await this._makeRequest("/widget/init",{method:"POST",body:JSON.stringify(t),headers:{"Content-Type":"application/json"}});return this.sessionToken=e.session_token,this.sessionExpiry=new Date(Date.now()+1e3*e.expires_in),this._storeSession(),{sessionToken:this.sessionToken,config:e.config||{},expiresIn:e.expires_in}}catch(e){throw new n(e.status||500,`Failed to initialize widget: ${e.message}`,e.response)}}async submitFeedback(e){if(this.isSessionValid()||await this.init(),!this.sessionToken)throw new n(401,"No valid session token available");const t={board:e.board_id||e.board||e.boardId,title:e.title,content:e.content,attachments:e.attachments||[]};try{return await this._makeRequest("/widget/feedback",{method:"POST",body:JSON.stringify(t),headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.sessionToken}`}})}catch(t){if(401===t.status)return this.sessionToken=null,this.sessionExpiry=null,await this.init(),this.submitFeedback(e);throw new n(t.status||500,`Failed to submit feedback: ${t.message}`,t.response)}}isSessionValid(){return this.sessionToken&&this.sessionExpiry&&new Date<this.sessionExpiry}setUserContext(e){this.userContext=e,"undefined"!=typeof localStorage&&localStorage.setItem("feedbackSDK_userContext",JSON.stringify(e))}getUserContext(){return this.userContext}clearSession(){this.sessionToken=null,this.sessionExpiry=null,"undefined"!=typeof localStorage&&(localStorage.removeItem("feedbackSDK_session"),localStorage.removeItem("feedbackSDK_userContext"))}_storeSession(){if("undefined"!=typeof localStorage)try{const e={token:this.sessionToken,expiry:this.sessionExpiry.toISOString(),workspace:this.workspace};localStorage.setItem("feedbackSDK_session",JSON.stringify(e))}catch(e){}}_loadStoredSession(){if("undefined"==typeof localStorage)return!1;try{const e=localStorage.getItem("feedbackSDK_session");if(!e)return!1;const t=JSON.parse(e);return this.sessionToken=t.token,this.sessionExpiry=new Date(t.expiry),this.isSessionValid()}catch(e){return!1}}async _makeRequest(e,t={}){const i=`${this.baseURL}${e}`;try{const e=await fetch(i,t);if(!e.ok){let t=`HTTP ${e.status}`,i=null;try{i=await e.json(),t=i.message||i.error||t}catch(n){t=await e.text()||t}throw new n(e.status,t,i)}const s=e.headers.get("content-type");return s&&s.includes("application/json")?await e.json():await e.text()}catch(e){if(e instanceof n)throw e;throw new n(0,e.message,null)}}}class r{constructor(){this.events=new Map}on(e,t){return this.events.has(e)||this.events.set(e,[]),this.events.get(e).push(t),()=>this.off(e,t)}off(e,t){const n=this.events.get(e);if(n){const e=n.indexOf(t);e>-1&&n.splice(e,1)}}emit(e,t){const n=this.events.get(e);n&&n.forEach(e=>{try{e(t)}catch(e){console.error("[FeedbackSDK] Event callback error:",e)}})}once(e,t){const n=this.on(e,e=>{t(e),n()});return n}clear(){this.events.clear()}getListenerCount(e){const t=this.events.get(e);return t?t.length:0}}function d(e="feedback"){return`${e}_${Date.now()}_${Math.random().toString(36).substring(2,9)}`}function c(e,t){const n={...e};for(const i in t)t.hasOwnProperty(i)&&(t[i]&&"object"==typeof t[i]&&!Array.isArray(t[i])?n[i]=c(e[i]||{},t[i]):n[i]=t[i]);return n}function l(){return"undefined"!=typeof window&&"undefined"!=typeof document}var h=Object.freeze({__proto__:null,debounce:function(e,t){let n;return function(...i){clearTimeout(n),n=setTimeout(()=>{clearTimeout(n),e(...i)},t)}},deepMerge:c,delay:function(e){return new Promise(t=>setTimeout(t,e))},escapeRegex:function(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")},formatFileSize:function(e){if(0===e)return"0 Bytes";const t=Math.floor(Math.log(e)/Math.log(1024));return parseFloat((e/Math.pow(1024,t)).toFixed(2))+" "+["Bytes","KB","MB","GB"][t]},generateId:d,getBrowserInfo:function(){return{userAgent:navigator.userAgent,platform:navigator.platform,language:navigator.language||navigator.userLanguage,cookieEnabled:navigator.cookieEnabled,screenResolution:`${screen.width}x${screen.height}`,windowSize:`${window.innerWidth}x${window.innerHeight}`,timezone:Intl.DateTimeFormat().resolvedOptions().timeZone}},getCSSProperty:function(e,t,n=""){if(!e||!t)return n;try{return window.getComputedStyle(e).getPropertyValue(t)||n}catch(e){return n}},getCurrentTimestamp:function(){return(new Date).toISOString()},getNestedProperty:function(e,t,n=void 0){if(!e||!t)return n;const i=t.split(".");let s=e;for(const e of i){if(null==s||!(e in s))return n;s=s[e]}return s},isBrowser:l,isInViewport:function(e){if(!e)return!1;const t=e.getBoundingClientRect();return t.top>=0&&t.left>=0&&t.bottom<=(window.innerHeight||document.documentElement.clientHeight)&&t.right<=(window.innerWidth||document.documentElement.clientWidth)},isMobile:function(){return!!l()&&(/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)||window.innerWidth<=768)},isValidEmail:function(e){return!(!e||"string"!=typeof e)&&/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e.trim())},safeJsonParse:function(e,t=null){try{return JSON.parse(e)}catch(e){return t}},sanitizeHTML:function(e){if(!e||"string"!=typeof e)return"";const t=document.createElement("div");return t.textContent=e,t.innerHTML},scrollToElement:function(e,t={}){if(!e)return;e.scrollIntoView({behavior:"smooth",block:"center",inline:"nearest",...t})},setNestedProperty:function(e,t,n){if(!e||!t)return e;const i=t.split("."),s=i.pop();let o=e;for(const e of i)e in o&&"object"==typeof o[e]||(o[e]={}),o=o[e];return o[s]=n,e},throttle:function(e,t){let n,i;return function(...s){i?(clearTimeout(n),n=setTimeout(()=>{Date.now()-i>=t&&(e(...s),i=Date.now())},t-(Date.now()-i))):(e(...s),i=Date.now())}},validateConfig:function(e,t=[]){const n=[];for(const i of t)e[i]||n.push(i);if(n.length>0)throw new Error(`Missing required configuration: ${n.join(", ")}`);return!0}});class p{constructor(e={}){this.id=e.id,this.sdk=e.sdk,this.apiService=e.apiService,this.type=e.type||"base",this.options={container:null,position:this.sdk.config.position,theme:this.sdk.config.theme,boardId:this.sdk.config.boardId,autoShow:!1,showBackdrop:!0,customStyles:{},...e},this.element=null,this.panelElement=null,this.backdropElement=null,this.mounted=!1,this.destroyed=!1,this.state={isOpen:!1,isSubmitting:!1,title:"",content:"",email:"",attachments:[],errors:{}},this._bindMethods()}mount(e){return this.mounted||this.destroyed||("string"==typeof e&&(e=document.querySelector(e)),e||(e=document.body),this.container=e,this.element=this._render(),this.container.appendChild(this.element),this.mounted=!0,this._attachEvents(),this.onMount(),this.options.autoShow&&this.show(),this.sdk.eventBus.emit("widget:mounted",{widget:this})),this}show(){return this.element&&(this.element.style.display="block"),this}hide(){return this.element&&(this.element.style.display="none"),this}openPanel(){this.state.isOpen=!0,this._renderPanel(),requestAnimationFrame(()=>{this.panelElement&&this.panelElement.classList.add("open"),this.backdropElement&&this.backdropElement.classList.add("show")})}closePanel(){this.panelElement&&this.panelElement.classList.remove("open"),this.backdropElement&&this.backdropElement.classList.remove("show"),setTimeout(()=>{this.state.isOpen=!1,this.panelElement&&this.panelElement.parentNode&&(this.panelElement.parentNode.removeChild(this.panelElement),this.panelElement=null),this.backdropElement&&this.backdropElement.parentNode&&(this.backdropElement.parentNode.removeChild(this.backdropElement),this.backdropElement=null),this._resetForm()},300)}async submitFeedback(){if(!this.state.isSubmitting){this._hideError();try{this.state.isSubmitting=!0,this._updateSubmitButton();const e={title:this.state.title||"Feedback",content:this.state.content,email:this.state.email,board_id:this.options.boardId,attachments:this.state.attachments};if(!this.state.content.trim())return void this._showError("Please enter your feedback message.");const t=await this.apiService.submitFeedback(e);this._showSuccessMessage(),this.closePanel(),this.sdk.eventBus.emit("feedback:submitted",{widget:this,feedback:t})}catch(e){this._showError("Failed to submit feedback. Please try again."),this.sdk.eventBus.emit("feedback:error",{widget:this,error:e})}finally{this.state.isSubmitting=!1,this._updateSubmitButton()}}}handleConfigUpdate(e){this.options.theme=e.theme,this.element&&this._updateTheme()}destroy(){this.destroyed||(this.onDestroy(),this.closePanel(),this.element&&this.element.parentNode&&this.element.parentNode.removeChild(this.element),this.destroyed=!0,this.mounted=!1,this.sdk.eventBus.emit("widget:destroyed",{widget:this}))}onMount(){}onDestroy(){}_render(){throw new Error("_render() must be implemented by concrete widget")}_attachEvents(){}_bindMethods(){this.openPanel=this.openPanel.bind(this),this.closePanel=this.closePanel.bind(this),this.submitFeedback=this.submitFeedback.bind(this)}_renderPanel(){if(this.panelElement)return;this.options.showBackdrop&&(this.backdropElement=document.createElement("div"),this.backdropElement.className="feedback-panel-backdrop",document.body.appendChild(this.backdropElement),this.backdropElement.addEventListener("click",this.closePanel)),this.panelElement=document.createElement("div"),this.panelElement.className=`feedback-panel theme-${this.options.theme}`,this.panelElement.innerHTML=this._getPanelHTML(),document.body.appendChild(this.panelElement),this._attachPanelEvents();const e=this.panelElement.querySelector("input, textarea");e&&setTimeout(()=>e.focus(),350)}_getPanelHTML(){return`\n <div class="feedback-panel-content">\n <div class="feedback-panel-header">\n <h3>Send Feedback</h3>\n <button class="feedback-panel-close" type="button" aria-label="Close">&times;</button>\n </div>\n <div class="feedback-panel-body">\n <form class="feedback-form">\n <div class="feedback-form-group">\n <label for="feedback-title-${this.id}">Title (optional)</label>\n <input \n type="text" \n id="feedback-title-${this.id}" \n name="title" \n placeholder="Brief description of your feedback"\n value="${this.state.title}"\n />\n </div>\n <div class="feedback-form-group">\n <label for="feedback-content-${this.id}">Message *</label>\n <textarea \n id="feedback-content-${this.id}" \n name="content" \n placeholder="Tell us what you think..."\n required\n >${this.state.content}</textarea>\n </div>\n <div class="feedback-error" role="alert"></div>\n <div class="feedback-form-actions">\n <button type="submit" class="feedback-btn feedback-btn-submit">\n ${this.state.isSubmitting?"Sending...":"Send Feedback"}\n </button>\n </div>\n </form>\n </div>\n </div>\n `}_attachPanelEvents(){const e=this.panelElement;e.querySelector(".feedback-panel-close").addEventListener("click",this.closePanel);e.querySelector(".feedback-form").addEventListener("submit",e=>{e.preventDefault(),this.submitFeedback()}),e.querySelector('input[name="title"]').addEventListener("input",e=>{this.state.title=e.target.value}),e.querySelector('textarea[name="content"]').addEventListener("input",e=>{this.state.content=e.target.value});const t=e=>{"Escape"===e.key&&(this.closePanel(),document.removeEventListener("keydown",t))};document.addEventListener("keydown",t)}_updateSubmitButton(){if(this.panelElement){const e=this.panelElement.querySelector(".feedback-btn-submit");e&&(e.textContent=this.state.isSubmitting?"Sending...":"Send Feedback",e.disabled=this.state.isSubmitting)}}_showError(e){if(this.panelElement){const t=this.panelElement.querySelector(".feedback-error");t&&(t.textContent=e,t.classList.add("show"))}}_hideError(){if(this.panelElement){const e=this.panelElement.querySelector(".feedback-error");e&&e.classList.remove("show")}}_showSuccessMessage(){const e=document.createElement("div");e.className="feedback-success-notification",e.innerHTML='\n <div class="feedback-success-content">\n <div class="feedback-success-icon">✓</div>\n <span>Feedback submitted successfully!</span>\n <button class="feedback-success-close" aria-label="Close">&times;</button>\n </div>\n ',document.body.appendChild(e);const t=()=>{e.parentNode&&(e.style.opacity="0",setTimeout(()=>{e.parentNode&&e.parentNode.removeChild(e)},300))};e.querySelector(".feedback-success-close").addEventListener("click",t),setTimeout(t,4e3)}_resetForm(){this.state.title="",this.state.content="",this.state.email="",this.state.errors={}}_updateTheme(){this.element&&(this.element.className=this.element.className.replace(/theme-\w+/,`theme-${this.options.theme}`)),this.panelElement&&(this.panelElement.className=this.panelElement.className.replace(/theme-\w+/,`theme-${this.options.theme}`))}openModal(){this.openPanel()}closeModal(){this.closePanel()}}class u extends p{constructor(e){super({...e,type:"button"})}_render(){const e=document.createElement("div");return e.className=`feedback-widget feedback-widget-button theme-${this.options.theme} position-${this.options.position}`,e.innerHTML='\n <button class="feedback-trigger-btn" type="button">\n <svg width="20" height="20" viewBox="0 0 20 20" fill="currentColor">\n <path d="M2.003 5.884L10 9.882l7.997-3.998A2 2 0 0016 4H4a2 2 0 00-1.997 1.884z"/>\n <path d="M18 8.118l-8 4-8-4V14a2 2 0 002 2h12a2 2 0 002-2V8.118z"/>\n </svg>\n Feedback\n </button>\n ',this.options.customStyles&&Object.assign(e.style,this.options.customStyles),e}_attachEvents(){const e=this.element.querySelector(".feedback-trigger-btn");e.addEventListener("click",this.openPanel),e.addEventListener("mouseenter",()=>{this.state.isSubmitting||(e.style.transform="translateY(-2px)")}),e.addEventListener("mouseleave",()=>{e.style.transform="translateY(0)"})}updateText(e){const t=this.element?.querySelector(".feedback-trigger-btn");if(t){const n=t.childNodes[t.childNodes.length-1];n&&n.nodeType===Node.TEXT_NODE&&(n.textContent=e)}}updatePosition(e){this.options.position=e,this.element&&(this.element.className=this.element.className.replace(/position-\w+-\w+/,`position-${e}`))}}class f extends p{constructor(e){super({...e,type:"inline"})}_render(){const e=document.createElement("div");return e.className=`feedback-widget feedback-widget-inline theme-${this.options.theme}`,e.innerHTML=`\n <div class="feedback-inline-content">\n <h3>Send us your feedback</h3>\n <form class="feedback-inline-form">\n <div class="feedback-form-group">\n <input \n type="text" \n name="title" \n placeholder="Title (optional)"\n value="${this.state.title}"\n />\n </div>\n <div class="feedback-form-group">\n <textarea \n name="content" \n placeholder="Your feedback..."\n required\n >${this.state.content}</textarea>\n </div>\n <div class="feedback-form-group">\n <input \n type="email" \n name="email" \n placeholder="Email (optional)"\n value="${this.state.email}"\n />\n </div>\n <button type="submit" class="feedback-btn feedback-btn-submit">\n Send Feedback\n </button>\n <div class="feedback-error" style="display: none;"></div>\n </form>\n </div>\n `,this.options.customStyles&&Object.assign(e.style,this.options.customStyles),e}_attachEvents(){const e=this.element.querySelector(".feedback-inline-form");e.addEventListener("submit",e=>{e.preventDefault(),this.submitFeedback()}),e.querySelector('input[name="title"]').addEventListener("input",e=>{this.state.title=e.target.value}),e.querySelector('textarea[name="content"]').addEventListener("input",e=>{this.state.content=e.target.value}),e.querySelector('input[name="email"]').addEventListener("input",e=>{this.state.email=e.target.value})}openModal(){const e=this.element.querySelector('textarea[name="content"]');e&&e.focus()}closeModal(){}_showSuccessMessage(){const e=this.element.querySelector(".feedback-inline-content"),t=e.innerHTML;e.innerHTML='\n <div class="feedback-success">\n <div class="feedback-success-icon">✓</div>\n <h3>Thank you!</h3>\n <p>Your feedback has been submitted successfully.</p>\n <button class="feedback-btn feedback-btn-reset">Send Another</button>\n </div>\n ';e.querySelector(".feedback-btn-reset").addEventListener("click",()=>{e.innerHTML=t,this._attachEvents(),this._resetForm()})}_showError(e){const t=this.element.querySelector(".feedback-error");t&&(t.textContent=e,t.style.display="block",setTimeout(()=>{t&&(t.style.display="none")},5e3))}_updateSubmitButton(){const e=this.element.querySelector(".feedback-btn-submit");e&&(e.textContent=this.state.isSubmitting?"Sending...":"Send Feedback",e.disabled=this.state.isSubmitting)}updateTitle(e){const t=this.element?.querySelector("h3");t&&(t.textContent=e)}setPlaceholder(e,t){const n=this.element?.querySelector(`[name="${e}"]`);n&&(n.placeholder=t)}}class b extends p{constructor(e){super({...e,type:"tab"})}_render(){const e=document.createElement("div");return e.className=`feedback-widget feedback-widget-tab theme-${this.options.theme} position-${this.options.position}`,e.innerHTML='\n <div class="feedback-tab-trigger">\n <span class="feedback-tab-text">Feedback</span>\n </div>\n ',this.options.customStyles&&Object.assign(e.style,this.options.customStyles),e}_attachEvents(){const e=this.element.querySelector(".feedback-tab-trigger");e.addEventListener("click",this.openModal),e.addEventListener("mouseenter",()=>{this.state.isSubmitting||(e.style.transform=this._getHoverTransform())}),e.addEventListener("mouseleave",()=>{e.style.transform="none"})}_getHoverTransform(){const e=this.options.position;return e.includes("right")?"translateX(-5px)":e.includes("left")?"translateX(5px)":"none"}updateText(e){const t=this.element?.querySelector(".feedback-tab-text");t&&(t.textContent=e)}updatePosition(e){this.options.position=e,this.element&&(this.element.className=this.element.className.replace(/position-\w+-\w+/,`position-${e}`))}}class m{static widgets=new Map([["button",u],["tab",b],["inline",f]]);static register(e,n){if("string"!=typeof e||!e.trim())throw new t("Widget type must be a non-empty string");if("function"!=typeof n)throw new t("Widget class must be a constructor function");this.widgets.set(e,n)}static create(e,n={}){const i=this.widgets.get(e);if(!i){const n=Array.from(this.widgets.keys()).join(", ");throw new t(`Unknown widget type: ${e}. Available types: ${n}`)}try{return new i(n)}catch(n){throw new t(`Failed to create widget of type '${e}': ${n.message}`,n)}}static getAvailableTypes(){return Array.from(this.widgets.keys())}static isTypeRegistered(e){return this.widgets.has(e)}static unregister(e){return this.widgets.delete(e)}static clear(){this.widgets.clear()}static getWidgetClass(e){return this.widgets.get(e)}}class g{constructor(e={}){this.config=this._validateAndMergeConfig(e),this.initialized=!1,this.widgets=new Map,this.eventBus=new r,this.apiService=new a({apiUrl:this.config.apiUrl,workspace:this.config.workspace,userContext:this.config.userContext}),this._bindMethods()}async init(){if(this.initialized)return{alreadyInitialized:!0};try{const e=await this.apiService.init(this.config.userContext);return e.config&&(this.config=c(this.config,e.config)),this.initialized=!0,this.eventBus.emit("sdk:initialized",{config:this.config,sessionToken:e.sessionToken}),{initialized:!0,config:e.config||{},sessionToken:e.sessionToken,expiresIn:e.expiresIn}}catch(e){throw this.eventBus.emit("sdk:error",{error:e}),new t(`Failed to initialize SDK: ${e.message}`,e)}}createWidget(e="button",n={}){if(!this.initialized)throw new t("SDK must be initialized before creating widgets. Call init() first.");const i=d("widget"),s={id:i,sdk:this,apiService:this.apiService,...this.config,...n};try{const t=m.create(e,s);return this.widgets.set(i,t),this.eventBus.emit("widget:created",{widget:t,type:e}),t}catch(e){throw new t(`Failed to create widget: ${e.message}`,e)}}getWidget(e){return this.widgets.get(e)}getAllWidgets(){return Array.from(this.widgets.values())}destroyWidget(e){const t=this.widgets.get(e);return!!t&&(t.destroy(),this.widgets.delete(e),this.eventBus.emit("widget:removed",{widgetId:e}),!0)}destroyAllWidgets(){for(const e of this.widgets.values())e.destroy();this.widgets.clear(),this.eventBus.emit("widgets:cleared")}updateConfig(e){const t={...this.config};this.config=this._validateAndMergeConfig(e,this.config);for(const e of this.widgets.values())e.handleConfigUpdate(this.config);this.eventBus.emit("config:updated",{oldConfig:t,newConfig:this.config})}setUserContext(e){this.config.userContext=e,this.apiService&&this.apiService.setUserContext(e),this.eventBus.emit("user:updated",{userContext:e})}getUserContext(){return this.config.userContext||(this.apiService?this.apiService.getUserContext():null)}async reinitialize(e=null){return this.apiService.clearSession(),this.initialized=!1,e&&this.setUserContext(e),this.init()}on(e,t){return this.eventBus.on(e,t),this}off(e,t){return this.eventBus.off(e,t),this}once(e,t){return this.eventBus.once(e,t),this}emit(e,t){return this.eventBus.emit(e,t),this}destroy(){this.destroyAllWidgets(),this.eventBus.removeAllListeners(),this.apiService.clearSession(),this.initialized=!1,this.eventBus.emit("sdk:destroyed")}_validateAndMergeConfig(e,t={}){const n=c(c({apiUrl:null,workspace:null,userContext:null,position:"bottom-right",theme:"light",boardId:"general",autoShow:!0,debug:!1},t),e);if(!n.workspace)throw new s("Missing required configuration: workspace");return n.userContext&&this._validateUserContext(n.userContext),n}_validateUserContext(e){if(!e.user_id&&!e.email)throw new s("User context must include at least user_id or email");const t={user_id:"string",email:"string",name:"string",custom_fields:"object",company:"object"};for(const[n,i]of Object.entries(t))if(e[n]&&typeof e[n]!==i)throw new s(`User context field '${n}' must be of type '${i}'`)}_bindMethods(){this.createWidget=this.createWidget.bind(this),this.destroyWidget=this.destroyWidget.bind(this),this.updateConfig=this.updateConfig.bind(this)}static create(e){return new g(e)}static async createAndInit(e){const t=new g(e);return await t.init(),t}static extractUserContextFromAuth(e){return e?{user_id:e.sub||e.id||e.user_id,email:e.email,name:e.name||e.display_name||e.full_name,custom_fields:{role:e.role,plan:e.plan||e.subscription?.plan,...e.custom_fields||{}},company:e.company||e.organization?{id:e.company?.id||e.organization?.id,name:e.company?.name||e.organization?.name,monthly_spend:e.company?.monthly_spend}:void 0}:null}}function k(){if("undefined"!=typeof document&&!document.querySelector("#feedback-sdk-styles")){const e=document.createElement("style");e.id="feedback-sdk-styles",e.textContent="\n.feedback-widget {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', Oxygen, Ubuntu, Cantarell, sans-serif;\n font-size: 14px;\n line-height: 1.4;\n z-index: 999999;\n box-sizing: border-box;\n}\n\n.feedback-widget *,\n.feedback-widget *::before,\n.feedback-widget *::after {\n box-sizing: border-box;\n}\n\n.feedback-widget-button {\n position: fixed;\n z-index: 999999;\n}\n\n.feedback-widget-button.position-bottom-right {\n bottom: 20px;\n right: 20px;\n}\n\n.feedback-widget-button.position-bottom-left {\n bottom: 20px;\n left: 20px;\n}\n\n.feedback-widget-button.position-top-right {\n top: 20px;\n right: 20px;\n}\n\n.feedback-widget-button.position-top-left {\n top: 20px;\n left: 20px;\n}\n\n.feedback-trigger-btn {\n position: relative;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 12px;\n height: 44px;\n overflow: hidden;\n border-radius: 0.5rem;\n border: none;\n padding: 10px 16px;\n font-size: 14px;\n font-weight: 500;\n font-family: inherit;\n cursor: pointer;\n transition: all 0.3s ease;\n color: white;\n background: #155EEF;\n box-shadow: 0 1px 2px 0 rgba(16, 24, 40, 0.05);\n}\n\n.feedback-trigger-btn:hover:not(:disabled) {\n background: #004EEB;\n box-shadow: 0 1px 2px 0 rgba(16, 24, 40, 0.1);\n}\n\n.feedback-trigger-btn:disabled {\n opacity: 0.7;\n cursor: not-allowed;\n}\n\n.feedback-trigger-btn:focus-visible {\n outline: 2px solid #155EEF;\n outline-offset: 2px;\n}\n\n/* Side Panel Styles */\n.feedback-panel {\n position: fixed;\n bottom: 80px;\n right: 24px;\n width: 420px;\n max-height: 500px;\n z-index: 1000000;\n transform: translateX(calc(100% + 24px));\n transition: transform 0.35s cubic-bezier(0.4, 0, 0.2, 1);\n font-family: inherit;\n}\n\n.feedback-panel.open {\n transform: translateX(0);\n}\n\n.feedback-panel-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.1);\n opacity: 0;\n transition: opacity 0.3s ease;\n pointer-events: none;\n z-index: 999999;\n}\n\n.feedback-panel-backdrop.show {\n opacity: 1;\n pointer-events: auto;\n}\n\n.feedback-panel-content {\n background: white;\n height: 100%;\n display: flex;\n flex-direction: column;\n border-radius: 16px;\n box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), \n 0 10px 10px -5px rgba(0, 0, 0, 0.04),\n 0 0 0 1px rgba(0, 0, 0, 0.05);\n}\n\n.feedback-panel.theme-dark .feedback-panel-content {\n background: #1F2937;\n color: white;\n}\n\n.feedback-panel-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 24px;\n border-bottom: 1px solid #E5E7EB;\n flex-shrink: 0;\n}\n\n.feedback-panel.theme-dark .feedback-panel-header {\n border-bottom-color: #374151;\n}\n\n.feedback-panel-header h3 {\n margin: 0;\n font-size: 18px;\n font-weight: 600;\n color: #111827;\n}\n\n.feedback-panel.theme-dark .feedback-panel-header h3 {\n color: white;\n}\n\n.feedback-panel-close {\n background: none;\n border: none;\n font-size: 24px;\n cursor: pointer;\n color: #6B7280;\n padding: 4px;\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 6px;\n transition: all 0.2s ease;\n}\n\n.feedback-panel-close:hover {\n background: #F3F4F6;\n color: #111827;\n}\n\n.feedback-panel-close:focus-visible {\n outline: 2px solid #155EEF;\n outline-offset: 2px;\n}\n\n.feedback-panel.theme-dark .feedback-panel-close {\n color: #9CA3AF;\n}\n\n.feedback-panel.theme-dark .feedback-panel-close:hover {\n background: #374151;\n color: white;\n}\n\n.feedback-panel-body {\n flex: 1;\n overflow-y: auto;\n padding: 24px;\n}\n\n.feedback-form {\n display: flex;\n flex-direction: column;\n height: 100%;\n}\n\n.feedback-form-group {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-bottom: 20px;\n}\n\n.feedback-form-group:last-child {\n margin-bottom: 0;\n}\n\n.feedback-form-group label {\n font-size: 14px;\n font-weight: 500;\n line-height: 1.25;\n color: #374151;\n}\n\n.feedback-panel.theme-dark .feedback-form-group label {\n color: #D1D5DB;\n}\n\n.feedback-form-group input {\n height: 44px;\n width: 100%;\n border-radius: 8px;\n border: 1px solid #D1D5DB;\n padding: 10px 14px;\n font-size: 15px;\n font-weight: 400;\n line-height: 1.5;\n color: #1F2937;\n font-family: inherit;\n outline: none;\n transition: all 0.2s ease;\n}\n\n.feedback-form-group input::placeholder {\n font-size: 15px;\n color: #9CA3AF;\n}\n\n.feedback-form-group input:focus {\n border-color: #155EEF;\n box-shadow: 0 0 0 3px rgba(21, 94, 239, 0.1);\n}\n\n.feedback-form-group input:focus-visible {\n outline: none;\n}\n\n.feedback-form-group textarea {\n min-height: 200px;\n width: 100%;\n resize: vertical;\n border-radius: 8px;\n border: 1px solid #D1D5DB;\n padding: 10px 14px;\n font-size: 15px;\n font-weight: 400;\n line-height: 1.5;\n color: #1F2937;\n font-family: inherit;\n outline: none;\n transition: all 0.2s ease;\n}\n\n.feedback-form-group textarea::placeholder {\n font-size: 15px;\n color: #9CA3AF;\n}\n\n.feedback-form-group textarea:focus {\n border-color: #155EEF;\n box-shadow: 0 0 0 3px rgba(21, 94, 239, 0.1);\n}\n\n.feedback-form-group textarea:focus-visible {\n outline: none;\n}\n\n.feedback-panel.theme-dark .feedback-form-group input,\n.feedback-panel.theme-dark .feedback-form-group textarea {\n background: #374151;\n border-color: #4B5563;\n color: white;\n}\n\n.feedback-panel.theme-dark .feedback-form-group input::placeholder,\n.feedback-panel.theme-dark .feedback-form-group textarea::placeholder {\n color: #6B7280;\n}\n\n.feedback-btn {\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n overflow: hidden;\n border-radius: 8px;\n border: none;\n height: 44px;\n padding: 10px 18px;\n font-size: 15px;\n font-weight: 500;\n font-family: inherit;\n cursor: pointer;\n transition: all 0.2s ease;\n}\n\n.feedback-btn:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.feedback-btn:focus-visible {\n outline: 2px solid #155EEF;\n outline-offset: 2px;\n}\n\n.feedback-btn-submit {\n background: #155EEF;\n color: white;\n width: 100%;\n}\n\n.feedback-btn-submit:hover:not(:disabled) {\n background: #1A56DB;\n}\n\n.feedback-btn-submit:active:not(:disabled) {\n background: #1E429F;\n}\n\n.feedback-btn-cancel {\n background: transparent;\n color: #6B7280;\n border: 1px solid #D1D5DB;\n}\n\n.feedback-btn-cancel:hover:not(:disabled) {\n background: #F9FAFB;\n border-color: #9CA3AF;\n color: #374151;\n}\n\n.feedback-panel.theme-dark .feedback-btn-cancel {\n color: #D1D5DB;\n border-color: #4B5563;\n}\n\n.feedback-panel.theme-dark .feedback-btn-cancel:hover:not(:disabled) {\n background: #374151;\n}\n\n.feedback-form-actions {\n display: flex;\n flex-direction: column;\n gap: 12px;\n margin-top: auto;\n padding-top: 24px;\n}\n\n.feedback-error {\n color: #DC2626;\n font-size: 14px;\n font-weight: 400;\n margin-top: 8px;\n padding: 12px;\n background: #FEE2E2;\n border: 1px solid #FECACA;\n border-radius: 8px;\n display: none;\n}\n\n.feedback-error.show {\n display: block;\n}\n\n.feedback-panel.theme-dark .feedback-error {\n background: #7F1D1D;\n border-color: #991B1B;\n color: #FCA5A5;\n}\n\n.feedback-success-notification {\n position: fixed;\n top: 24px;\n right: 24px;\n z-index: 1000002;\n background: white;\n border: 1px solid #D1FAE5;\n border-radius: 12px;\n box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);\n animation: slideInRight 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n min-width: 320px;\n}\n\n.feedback-success-content {\n display: flex;\n align-items: center;\n padding: 16px 20px;\n gap: 12px;\n}\n\n.feedback-success-icon {\n width: 20px;\n height: 20px;\n border-radius: 50%;\n background: #10B981;\n color: white;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 12px;\n font-weight: 600;\n flex-shrink: 0;\n}\n\n.feedback-success-content span {\n color: #065F46;\n font-weight: 500;\n font-size: 14px;\n flex: 1;\n}\n\n.feedback-success-close {\n background: none;\n border: none;\n color: #6B7280;\n cursor: pointer;\n font-size: 20px;\n padding: 0;\n width: 24px;\n height: 24px;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.2s ease;\n border-radius: 4px;\n flex-shrink: 0;\n}\n\n.feedback-success-close:hover {\n background: #F3F4F6;\n color: #374151;\n}\n\n.feedback-success-close:focus-visible {\n outline: 2px solid #155EEF;\n outline-offset: 2px;\n}\n\n@keyframes slideInRight {\n from {\n transform: translateX(400px);\n opacity: 0;\n }\n to {\n transform: translateX(0);\n opacity: 1;\n }\n}\n\n@keyframes fadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n.feedback-panel-backdrop {\n animation: fadeIn 0.3s ease;\n}\n\n@media (max-width: 768px) {\n .feedback-panel {\n width: 100%;\n top: auto;\n bottom: 0;\n right: 0;\n left: 0;\n height: 85vh;\n max-height: 85vh;\n transform: translateY(100%);\n border-radius: 20px 20px 0 0;\n }\n \n .feedback-panel.open {\n transform: translateY(0);\n }\n \n .feedback-panel-content {\n border-radius: 20px 20px 0 0;\n }\n \n .feedback-panel-header {\n padding: 20px;\n position: relative;\n }\n \n .feedback-panel-header::before {\n content: '';\n position: absolute;\n top: 8px;\n left: 50%;\n transform: translateX(-50%);\n width: 40px;\n height: 4px;\n background: #D1D5DB;\n border-radius: 2px;\n }\n \n .feedback-panel.theme-dark .feedback-panel-header::before {\n background: #4B5563;\n }\n \n .feedback-panel-body {\n padding: 20px;\n }\n \n .feedback-form-group textarea {\n min-height: 150px;\n }\n \n .feedback-widget-button {\n bottom: 16px;\n right: 16px;\n }\n \n .feedback-widget-button.position-bottom-left {\n left: 16px;\n }\n \n .feedback-success-notification {\n top: 16px;\n right: 16px;\n left: 16px;\n min-width: auto;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .feedback-trigger-btn,\n .feedback-btn,\n .feedback-panel,\n .feedback-panel-backdrop,\n .feedback-success-notification {\n transition: none;\n animation: none;\n }\n}\n\n@media print {\n .feedback-widget,\n .feedback-panel,\n .feedback-panel-backdrop,\n .feedback-success-notification {\n display: none !important;\n }\n}\n",document.head.appendChild(e)}}function w(){if("undefined"!=typeof window&&window.FeedbackSDKConfig){k();const e={...window.FeedbackSDKConfig},t=new g(e);t.init().then(n=>{if(window.FeedbackSDK.instance=t,window.FeedbackSDKConfig.autoCreate){(Array.isArray(window.FeedbackSDKConfig.autoCreate)?window.FeedbackSDKConfig.autoCreate:[window.FeedbackSDKConfig.autoCreate]).forEach(e=>{try{t.createWidget(e.type||"button",e).mount(e.container)}catch(e){console.error("[FeedbackSDK] Failed to create widget:",e)}})}if("undefined"!=typeof CustomEvent){const i=new CustomEvent("FeedbackSDKReady",{detail:{sdk:t,config:e,initData:n}});window.dispatchEvent(i)}}).catch(t=>{if(console.error("[FeedbackSDK] Auto-initialization failed:",t),"undefined"!=typeof CustomEvent){const n=new CustomEvent("FeedbackSDKError",{detail:{error:t,config:e,phase:"initialization"}});window.dispatchEvent(n)}})}}const x={FeedbackSDK:g,BaseWidget:p,ButtonWidget:u,TabWidget:b,InlineWidget:f,WidgetFactory:m,EventBus:r,APIService:a,SDKError:t,APIError:n,WidgetError:i,ConfigError:s,ValidationError:o,helpers:h,create:e=>(k(),new g(e)),version:"1.0.0",instance:null,isReady:()=>Boolean(x.instance),getInstance:()=>x.instance,setUserContext:e=>{x.instance?x.instance.setUserContext(e):"undefined"!=typeof window&&(window.FeedbackSDKUserContext=e)},initWithUser:async(e,t)=>{k();const n={...e,userContext:t},i=new g(n);return await i.init(),"undefined"!=typeof window&&(window.FeedbackSDK.instance=i),i},onReady:e=>{"undefined"!=typeof window&&(x.isReady()?e(x.instance):window.addEventListener("FeedbackSDKReady",t=>{e(t.detail.sdk,t.detail)},{once:!0}))},onError:e=>{"undefined"!=typeof window&&window.addEventListener("FeedbackSDKError",t=>{e(t.detail.error,t.detail)})},extractUserContext:g.extractUserContextFromAuth};"undefined"!=typeof window&&(window.FeedbackSDK=x,"undefined"!=typeof document&&("loading"===document.readyState?document.addEventListener("DOMContentLoaded",w):setTimeout(w,0))),e.APIError=n,e.APIService=a,e.BaseWidget=p,e.ButtonWidget=u,e.ConfigError=s,e.EventBus=r,e.FeedbackSDK=g,e.InlineWidget=f,e.SDKError=t,e.TabWidget=b,e.ValidationError=o,e.WidgetError=i,e.WidgetFactory=m,e.default=x,e.helpers=h,Object.defineProperty(e,"__esModule",{value:!0})});
2
2
  //# sourceMappingURL=feedback-sdk.min.js.map