{"version":3,"file":"groupShop.js","mappings":";;;;AAAA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,IAAI;AACJ;AACA;AACA;AACA;AACA;AACA,IAAI;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA,OAAO;AACP,KAAK;AACL;AACA;AACA,IAAI;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB,UAAU,GAAG,YAAY;AAC9C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA,IAAI;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA,8CAA8C,UAAU;AACxD;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA,mCAAmC,YAAY;AAC/C,8CAA8C,UAAU;AACxD;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,mDAAmD,SAAS;AAC5D;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA,WAAW;AACX;AACA;AACA,WAAW;AACX;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO;AACP,MAAM;AACN;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA,2BAA2B,EAAE;AAC7B;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,UAAU;AACV;AACA;AACA;AACA,WAAW;AACX;AACA,OAAO;AACP,KAAK;AACL;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA,OAAO;AACP,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA,IAAI;AACJ;AACA;AACA;;;ACnWmD;AACnD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,iBAAiB;AAC7B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA,YAAY,WAAW;AACvB,IAAI,QAAQ;AACZ,IAAI,QAAQ;AACZ,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,4BAA4B,qCAAqC;AACjE,GAAG;AACH;AACA;AACA;AACA,YAAY,OAAO;AACnB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO;AACP,MAAM;AACN;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA,YAAY,UAAU;AACtB;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO;AACP,KAAK;AACL,GAAG;AACH;AACA;AACA,iDAAe,UAAU,EAAC;;;AChH+B;;AAEzD;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,gDAAgD,UAAU;AAC1D,WAAW;;AAEX;AACA;AACA;;AAEA;AACA,+BAA+B;;AAE/B;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA,UAAU;AACV;AACA;AACA;AACA;AACA,WAAW;AACX;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA,SAAS;AACT,OAAO;;AAEP;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA,SAAS;AACT,OAAO;AACP;AACA,GAAG;;AAEH;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA,aAAa;AACb,WAAW;AACX,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,0FAA0F,UAAU;AACpG;AACA;AACA;AACA;AACA;AACA;;AAEA,aAAa;AACb,WAAW;AACX,OAAO;AACP;AACA,GAAG;;AAEH;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA,kEAAkE,oCAAoC;AACtG,qCAAqC,WAAW;AAChD,YAAY;AACZ,oCAAoC,WAAW,qBAAqB;AACpE;AACA;AACA;AACA,KAAK;AACL,GAAG;;AAEH;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA,SAAS;;AAET;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA,eAAe;AACf;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA,aAAa;AACb,WAAW;AACX,SAAS;AACT;AACA,GAAG;;AAEH;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA,cAAc;AACd;AACA;;AAEA;AACA;AACA;AACA,qEAAqE,YAAY;AACjF,aAAa;AACb,WAAW;AACX,SAAS;;AAET;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA,WAAW;AACX,SAAS;AACT;AACA,GAAG;;AAEH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL,GAAG;;AAEH;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA,GAAG;AACH;;AAEA,qDAAe,cAAc,EAAC;;;ACzS9B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iDAAiD;AACjD,6CAA6C;AAC7C,6CAA6C,8BAA8B;AAC3E;AACA;AACA,2CAA2C,sBAAsB;AACjE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA;AACA;AACA;AACA;AACA,mDAAe,WAAW,EAAC;;;AChEmB;AACe;AACpB;AACO;;AAEhD;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA,KAAK;;AAEL;AACA;AACA;AACA,MAAM;AACN;AACA;AACA;AACA,GAAG;;AAEH;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA,iBAAiB;AACjB;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,UAAU;AACV;AACA;AACA;AACA,KAAK;;AAEL;AACA,0BAA0B,kBAAkB;AAC5C;AACA;AACA;;AAEA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA,WAAW;AACX;AACA,SAAS;AACT;AACA;;AAEA;AACA;AACA;AACA;AACA,YAAY;AACZ;AACA;;AAEA,UAAU;AACV;AACA;AACA;AACA,YAAY;AACZ;AACA;AACA;;AAEA,QAAQ;AACR;AACA;;AAEA;AACA;AACA,UAAU;AACV;AACA;AACA;;AAEA;AACA;AACA,OAAO;;AAEP;AACA;AACA,GAAG;;AAEH;AACA;AACA;AACA;AACA;;AAEA;AACA,uBAAuB,cAAc;AACrC;;AAEA;;AAEA;AACA;AACA,sEAAsE,YAAW;AACjF;AACA;AACA;AACA;AACA;AACA;;AAEA;;AAEA,qBAAqB,iBAAiB;;AAEtC;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA,+DAA+D;AAC/D,gEAAgE;AAChE,WAAW;AACX;AACA,SAAS;;AAET;;AAEA;;AAEA;AACA;AACA,UAAU;AACV;AACA;;AAEA,QAAQ;AACR;AACA;;AAEA;AACA;AACA,UAAU;AACV;AACA;AACA;;AAEA;AACA;AACA,GAAG;;AAEH;AACA;;AAEA;AACA;AACA;AACA;AACA,OAAO;;AAEP;AACA;AACA;AACA,8DAA8D;AAC9D,6DAA6D;AAC7D,SAAS;AACT;AACA,OAAO;;AAEP;AACA;AACA;AACA;AACA,UAAU,UAAU;AACpB;AACA,QAAQ;AACR;AACA,UAAU,UAAU;AACpB;AACA;;AAEA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA;AACA,GAAG;;AAEH;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA,aAAa;AACb,GAAG;;AAEH;AACA;;AAEA;AACA;AACA,MAAM;AACN,2CAA2C,QAAQ;AACnD;AACA;AACA;AACA;AACA,GAAG;;AAEH;AACA;;AAEA;AACA;;AAEA;;AAEA;AACA;AACA;AACA,OAAO;;AAEP;;AAEA;AACA;AACA,UAAU,UAAU;AACpB,UAAU;AACV;AACA;AACA,WAAW;AACX;AACA,QAAQ;AACR;AACA,UAAU,UAAU;AACpB,UAAU;AACV;AACA;AACA,WAAW;AACX;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO;;AAEP;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO;AACP;AACA,MAAM;AACN;AACA;AACA;AACA;AACA,SAAS;;AAET;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;;AAEA,4CAAe,KAAK,EAAC;;;ACnVkB;AACc;AACrD;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,6CAA6C;AACzD;AACA;AACA;AACA;AACA,OAAO;AACP;AACA;AACA;AACA,QAAQ;AACR;AACA;AACA,MAAM;AACN;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA,wBAAwB,2CAA2C;AACnE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA,YAAY,mBAAmB;AAC/B;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,UAAU;AACV;AACA;AACA;AACA,YAAY;AACZ;AACA;AACA;AACA;AACA,OAAO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,sCAAsC,eAAe;AACrD,0BAA0B,WAAW;AACrC;AACA;AACA;AACA;AACA;AACA;AACA;AACA,oCAAoC,WAAW;AAC/C,mCAAmC,yBAAyB;AAC5D;AACA;AACA,0CAA0C,qBAAqB;AAC/D;AACA,YAAY;AACZ,UAAU;AACV;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,wBAAwB,2CAA2C;AACnE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,WAAW;AACX;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,WAAW;AACX;AACA,SAAS;AACT;AACA;AACA;AACA;AACA,UAAU,KAAK;AACf,UAAU;AACV;AACA,iFAAiF,gBAAgB;AACjG,UAAU,KAAK;AACf;AACA,QAAQ;AACR;AACA;AACA,QAAQ,KAAK;AACb;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO;AACP;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA,SAAS;AACT,OAAO;AACP,KAAK;AACL,GAAG;AACH;AACA;AACA,YAAY,yCAAyC;AACrD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,WAAW;AACX;AACA;AACA;AACA;AACA;AACA,SAAS;AACT,OAAO;AACP;AACA;AACA;AACA;AACA,OAAO;AACP;AACA,GAAG;AACH;AACA;AACA","sources":["webpack://build/../malupires/assets/javascripts/components/utilities.js","webpack://build/../malupires/assets/javascripts/common/cartDrawer.js","webpack://build/../malupires/assets/javascripts/pages/product/customizations.js","webpack://build/../malupires/assets/javascripts/components/uploadImages.js","webpack://build/../malupires/assets/javascripts/components/store.js","webpack://build/../malupires/assets/javascripts/groupShop.js"],"sourcesContent":["// ===============================================================\r\n// ADIÇÃO MANUAL DE ASSET\r\n// ===============================================================\r\n// Usado para incluir assets no código de forma manual, conforme a necessidade\r\nexport function addAsset(source, onloadCallback) {\r\n if (!source || source === '') return console.error(`addAsset: caminho não definido.`);\r\n\r\n if (source.includes('.js')) {\r\n const scriptTag = document.createElement('script');\r\n scriptTag.setAttribute('src', source);\r\n\r\n if (onloadCallback && typeof onloadCallback === 'function') {\r\n scriptTag.onload = onloadCallback;\r\n }\r\n\r\n return document.body.appendChild(scriptTag);\r\n } else if (source.includes('.css')) {\r\n const linkTag = document.createElement('link');\r\n linkTag.setAttribute('rel', 'stylesheet');\r\n linkTag.setAttribute('type', 'text/css');\r\n linkTag.setAttribute('href', source);\r\n return document.head.appendChild(linkTag);\r\n } else {\r\n const error = `addAsset: Erro ao criar o asset. Tipo de script não definido, ou não possui tratamento para este tipo de asset.`;\r\n return console.error(error, source);\r\n }\r\n}\r\n\r\n// ===============================================================\r\n// BUSCA O CARRINHO ATIVO\r\n// ===============================================================\r\nexport async function getCart() {\r\n try {\r\n const response = await fetch('/carrinho', {\r\n headers: {\r\n Accept: 'application/json',\r\n },\r\n });\r\n\r\n return await response.json();\r\n } catch (error) {\r\n console.error('Erro ao buscar os dados do carrinho');\r\n console.error(error);\r\n return false;\r\n }\r\n}\r\n\r\n// ===============================================================\r\n// SERIALIZE ARRAY\r\n// ===============================================================\r\nexport function serializeArray(form) {\r\n const formData = new FormData(form);\r\n const data = {};\r\n\r\n for (const [name, value] of formData) {\r\n data[name] = value;\r\n }\r\n\r\n const formBody = [];\r\n\r\n for (const key in data) {\r\n const encodeKey = encodeURIComponent(key);\r\n const encodeValue = encodeURIComponent(data[key]);\r\n formBody.push(`${encodeKey}=${encodeValue}`);\r\n }\r\n\r\n return formBody.join('&');\r\n}\r\n\r\n// ===============================================================\r\n// URL ENCODE FORM DATA\r\n// ===============================================================\r\nexport function urlencodeFormData(formData) {\r\n let string = '';\r\n\r\n function encode(s) {\r\n return encodeURIComponent(s).replace(/%20/g, '+');\r\n }\r\n\r\n for (const pair of formData.entries()) {\r\n if (typeof pair[1] == 'string') {\r\n string += (string ? '&' : '') + encode(pair[0]) + '=' + encode(pair[1]);\r\n }\r\n }\r\n return string;\r\n}\r\n\r\n// ===============================================================\r\n// SLIDE TOGGLE\r\n// ===============================================================\r\nexport function slideToggle(contentWrapper, content, duration = 500) {\r\n let initialHeight = window.getComputedStyle(contentWrapper).height;\r\n\r\n if (initialHeight == '0px') {\r\n return slideDown(contentWrapper, content, duration);\r\n } else {\r\n return slideUp(contentWrapper, duration);\r\n }\r\n}\r\n\r\n// ===============================================================\r\n// SLIDE UP\r\n// ===============================================================\r\nexport function slideUp(contentWrapper, duration = 500) {\r\n contentWrapper.style.height = '0px';\r\n contentWrapper.style.transition = `height ${duration} ease`;\r\n}\r\n\r\n// ===============================================================\r\n// SLIDE DOWN\r\n// ===============================================================\r\nexport function slideDown(contentWrapper, content, duration = 500) {\r\n let innerHeight = content.clientHeight;\r\n\r\n contentWrapper.style.height = `${innerHeight}px`;\r\n contentWrapper.style.transition = `height ${duration} ease`;\r\n}\r\n\r\n// ===============================================================\r\n// UPDATE DISCOUNT IN PRODUCT BLOCK\r\n// ===============================================================\r\nexport function updatePriceBlock() {\r\n const priceEls = document.querySelectorAll('[data-init-price]');\r\n\r\n if (priceEls == null) return;\r\n\r\n priceEls.forEach((priceEl) => {\r\n const discount = priceEl.dataset.discountPercent;\r\n\r\n priceEl.dispatchEvent(new Event('change'));\r\n\r\n // discount\r\n if (discount != '0') {\r\n priceEl.style.setProperty('--discount', `'-${discount}%'`);\r\n }\r\n });\r\n}\r\n\r\n// ===============================================================\r\n// PREÇO POR AJAX\r\n// ===============================================================\r\nexport function getPriceProd() {\r\n var selectors = document.querySelectorAll('[data-update-price]');\r\n var attr = 'update-price';\r\n\r\n if (selectors.length() > 0) {\r\n selectors.forEach((selector) => {\r\n var prodId = selector.dataset(attr);\r\n var url = '/produto/preco/' + prodId;\r\n\r\n if (prodId != '' && prodId != null) {\r\n $.ajax({\r\n url: url,\r\n type: 'GET',\r\n })\r\n .done((resp) => {\r\n selector.innerHTML = resp;\r\n })\r\n .fail((resp) => {\r\n console.error(resp);\r\n });\r\n }\r\n });\r\n }\r\n}\r\n\r\n// ===============================================================\r\n// DEBOUNCE\r\n// ===============================================================\r\n/*\r\n Debounce retorna uma função que enquanto continuar sendo chamada não é executada\r\n A função só será executada quando para de ser chamada por N milisegundos\r\n Útil para melhorar a performance de códigos que são executados muitas vezes por segundo, como o $(window).resize()\r\n\r\n Ex:\r\n \r\n $(window).resize(debounce(function() {\r\n // código a ser executado\r\n }, 500))\r\n \r\n No exemplo acima a função só será executada 500ms depois do último resize\r\n Abra o link abaixo e redimensione a janela branca e acompanhe o output do console\r\n Exemplo codepen: https://codepen.io/valkervieira/pen/oNgqyWY\r\n\r\n Um caso comum de uso é em lojas onde a seleção de um filtro na página de tag recarrega automáticamente a página\r\n Com o debounce o usuário pode escolher vários filtros rapidamente e a página só recarrega quando parar de escolher\r\n*/\r\n\r\nexport function debounce(func, wait, immediate) {\r\n var timeout;\r\n immediate || (immediate = true);\r\n\r\n return function () {\r\n var context = this,\r\n args = arguments;\r\n\r\n var later = function () {\r\n timeout = null;\r\n if (!immediate) func.apply(context, args);\r\n };\r\n\r\n var callNow = immediate && !timeout;\r\n\r\n clearTimeout(timeout);\r\n\r\n timeout = setTimeout(later, wait);\r\n\r\n if (callNow) func.apply(context, args);\r\n };\r\n}\r\n\r\n// ===============================================================\r\n// THROTTLE\r\n// ===============================================================\r\n/*\r\n Throttle diminui a frequencia que uma função é executada\r\n Enquanto no debounce a função só é executada quando para de ser chamada, no throttle ela\r\n continua sendo executada só que em um intervalo mínimo de N milisegundos (default = 250)\r\n\r\n Ex:\r\n\r\n $(window).resize(throttle() {\r\n // código a ser executado\r\n }, 500)\r\n\r\n No exemplo acima a função resize é chamada várias vezes por segundo mas só é executada 1 vez a cada 500ms\r\n Abra o link abaixo, redimensione a janela branca e acompanhe o console\r\n Exemplo codepen: https://codepen.io/valkervieira/pen/yLyKEPW\r\n\r\n Um caso comum de uso é checar se o scroll passou de um determinado ponto, para fixar um header ou alterar algum elemento do DOM\r\n*/\r\nexport function throttle(fn, threshhold, scope) {\r\n threshhold || (threshhold = 250);\r\n var last, deferTimer;\r\n return function () {\r\n var context = scope || this;\r\n\r\n var now = +new Date(),\r\n args = arguments;\r\n if (last && now < last + threshhold) {\r\n // hold on to it\r\n clearTimeout(deferTimer);\r\n deferTimer = setTimeout(function () {\r\n last = now;\r\n fn.apply(context, args);\r\n }, threshhold);\r\n } else {\r\n last = now;\r\n fn.apply(context, args);\r\n }\r\n };\r\n}\r\n\r\n// ===============================================================\r\n// FORMAT MONEY\r\n// ===============================================================\r\nexport function formatMoney(value) {\r\n // FORMATA UM VALOR\r\n return (\r\n 'R$ ' +\r\n value\r\n .toFixed(2)\r\n .replace('.', ',')\r\n .replace(/(\\d)(?=(\\d{3})+\\,)/g, '$1.')\r\n );\r\n}\r\n\r\n// ===============================================================\r\n// FORMAT VALUE\r\n// ===============================================================\r\n\r\nexport const formatValue = function (value = '') {\r\n let parsedValue = value;\r\n if (typeof value === 'number') {\r\n parsedValue = value.toFixed(2).toString();\r\n }\r\n return parsedValue.replace('.', ',');\r\n};\r\n\r\n// ===============================================================\r\n// VALIDA QUANTIDADE\r\n// ===============================================================\r\nexport function validateQuantity(_val) {\r\n // VALIDA SE A QUANTIDADE INFORMADA É UM NÚMERO\r\n if (!isNaN(_val)) {\r\n if (parseInt(_val) > 0) {\r\n return true;\r\n }\r\n }\r\n\r\n return false;\r\n}\r\n\r\n// ===============================================================\r\n// CLEAR NUMBER\r\n// ===============================================================\r\nexport function getClearNumber(_val) {\r\n // RETORNA UM NÚMERO LIMPO COMO INT\r\n if (!isNaN(_val)) {\r\n var clearNumber = parseInt(_val);\r\n\r\n return clearNumber;\r\n }\r\n\r\n return false;\r\n}\r\n\r\n// ===============================================================\r\n// BUSCA\r\n// ===============================================================\r\nexport function setSearch() {\r\n const toggleButton = document.querySelectorAll('[data-toggle-search]');\r\n const search = document.querySelector('[data-search-container]');\r\n const input = document.querySelector('[data-search-input]');\r\n\r\n toggleButton.length > 0 &&\r\n toggleButton.forEach((button) => {\r\n button.addEventListener('click', () => {\r\n if (search.classList.contains('open')) {\r\n search.classList.remove('open');\r\n } else {\r\n search.classList.add('open');\r\n setTimeout(() => {\r\n input.focus();\r\n }, 400);\r\n }\r\n });\r\n });\r\n}\r\n\r\n// ===============================================================\r\n// BUSCA OS DADOS DO CLIENTE\r\n// ===============================================================\r\nexport async function getClient() {\r\n try {\r\n const response = await fetch('/conta/cliente', {\r\n headers: {\r\n accept: 'application/json',\r\n 'Content-Type': 'application/json',\r\n cache: 'no-store',\r\n },\r\n });\r\n const client = await response.json();\r\n const access = Object.keys(client).length > 0;\r\n\r\n if (access) {\r\n window.client = client;\r\n\r\n return client;\r\n } else {\r\n return null;\r\n }\r\n } catch (error) {\r\n console.error(`getClient error`, error);\r\n }\r\n}\r\n","import { addAsset } from \"../components/utilities\";\r\n\r\nconst CartDrawer = {\r\n root: document.querySelector('#component-cart-drawer-root'),\r\n buttons: document.querySelectorAll('[data-toggle-cart]'),\r\n countWrapper: document.querySelector('[data-cart-count]'),\r\n settings: window.cartDrawerSettings || false,\r\n\r\n setCartDrawer: function() {\r\n const { settings, root } = CartDrawer;\r\n\r\n if (!root || !settings) return;\r\n \r\n // Define frete grátis\r\n const freeShipping = (settings.freeShippingValue > 1) ? settings.freeShippingValue : 0\r\n\r\n // Inicia o componente\r\n const componentCartDrawer = new Vnda.Component.CartDrawer({\r\n anchor: 'right',\r\n display: 'list',\r\n startOpen: false,\r\n titleCart: 'Carrinho de compras',\r\n disableShippingCalculation: true,\r\n suggestedProductsTag: 'sugestao',\r\n freeShipping\r\n });\r\n\r\n // Renderiza o componente\r\n componentCartDrawer.render(root);\r\n\r\n // Salva instância para acesso global\r\n window.cartDrawerSettings.instance = componentCartDrawer\r\n CartDrawer.settings = window.cartDrawerSettings\r\n\r\n // dispara evento de carregamento, escutado por CartDrawer.show()\r\n root.dispatchEvent(new Event('vnda:cart-drawer-loaded'))\r\n },\r\n\r\n loadComponent: function() {\r\n const { settings } = CartDrawer;\r\n addAsset(settings.script, CartDrawer.setCartDrawer);\r\n addAsset(settings.styles);\r\n },\r\n\r\n handleCartButton: function(button) {\r\n // Evita múltiplos cliques caso o carrinho precisa ser instanciado primeiro\r\n if (button.classList.contains('-loading')) {\r\n return;\r\n }\r\n\r\n // Abre o cart drawer\r\n button.classList.add('-loading');\r\n CartDrawer.show(() => { button.classList.remove('-loading') })\r\n },\r\n\r\n show: function (callback) {\r\n\r\n const { root } = CartDrawer;\r\n\r\n // No mobile, fecha o menu primeiro\r\n if (window.mmenu) window.mmenu.close()\r\n\r\n // Instancia o componente, caso ainda não exista\r\n if (!CartDrawer.settings.instance) CartDrawer.loadComponent();\r\n\r\n // Observa criação da instância inicial, caso não tenha\r\n if (CartDrawer.settings.instance === false) {\r\n root.addEventListener('vnda:cart-drawer-loaded', () => {\r\n CartDrawer.settings.instance.open()\r\n if (typeof callback === 'function') callback()\r\n })\r\n } else {\r\n // Já possui cart drawer instanciado, retorna abertura\r\n CartDrawer.settings.instance.open();\r\n if (typeof callback === 'function') callback()\r\n }\r\n },\r\n\r\n getCartItens: async function () {\r\n try {\r\n const response = await fetch('/carrinho/itens');\r\n const itens = await response.json()\r\n return itens;\r\n\r\n } catch (error) {\r\n console.error('Erro ao buscar a quantidade de produtos do carrinho');\r\n console.error(error);\r\n }\r\n },\r\n\r\n updateCartCount: async function (_itemsCount = null) {\r\n let items = _itemsCount;\r\n if (_itemsCount == null) items = await CartDrawer.getCartItens();\r\n\r\n this.countWrapper.innerHTML = items;\r\n },\r\n\r\n init: function () {\r\n const _this = this;\r\n const { buttons } = _this;\r\n\r\n // Atualiza o contador de itens do carrinho\r\n _this.updateCartCount();\r\n\r\n if (buttons.length > 0) buttons.forEach(button => {\r\n button.addEventListener('click', () => {\r\n _this.handleCartButton(button)\r\n })\r\n })\r\n },\r\n};\r\n\r\nexport default CartDrawer;\r\n","import { formatMoney } from '../../components/utilities';\n\nconst Customizations = {\n // Variável utilizada para identificar os inputs que devem receber o link de imagem\n fileInputs: window.fileInputs,\n // Lida do personalização de arquivos\n customizationsFile: function (currentProduct) {\n if (!currentProduct) return;\n\n // Verifica elementos de personalização 'Fotos'\n const inputsFile = currentProduct.querySelectorAll('[data-customizations-file]');\n const dropZones = currentProduct.querySelectorAll('[data-customizations-dropzone]');\n\n if (inputsFile.length > 0 && dropZones.length > 0) {\n // Inicia configurações do webform\n let inputCount = 0;\n\n // Lida com envio de arquivos via webform\n const handleFiles = (input, files, warning, error, display) => {\n // Verifica formato dos arquivos\n let wrongFiles = files.filter(\n (file) => file.type != 'image/png' && file.type != 'image/jpeg' && file.type != 'application/pdf'\n );\n if (!wrongFiles.length) {\n // Armazena nome(s) do(s) arquivo(s)\n let names = '';\n files.forEach((file, i) => {\n names += i == 0 ? file.name : ` | ${file.name}`;\n });\n\n // Atualiza campo da personalização de texto\n input.dispatchEvent(new Event('input'));\n input.value = names;\n\n // Cria um objeto do input com o elemento e seus arquivos\n const inputObject = {input: input, files: files}\n\n // Insere/substitui o objeto no array global com o elemento e arquivos do input\n Customizations.fileInputs[inputCount] = inputObject;\n\n // Atualiza contador de campos do webform\n inputCount++;\n\n // Atualiza elementos informativos\n if (display) display.innerHTML = names;\n if (warning) warning.classList.add('-active');\n\n // Remove campo de arquivos do webform ao editar campo da personalização de texto\n const removeInput = () => {\n // Deleta do array de objetos de inputs caso exista\n if (Customizations.fileInputs[inputCount]) {\n delete Customizations.fileInputs[inputCount];\n }\n \n // Atualiza contador e desabilita envio\n inputCount--;\n\n // Atualiza elementos informativos\n if (display) display.innerHTML = 'Arraste ou solte os arquivos';\n if (warning) warning.classList.remove('-active');\n\n input.removeEventListener('input', removeInput);\n };\n input.addEventListener('input', removeInput);\n } else if (error) {\n // Exibe mensagem de erro referente ao formato dos arquivos\n error.classList.add('-active');\n setTimeout(() => {\n error.classList.remove('-active');\n }, 5000);\n }\n };\n\n // Lida com campo para selecionar arquivos\n inputsFile.forEach((inputFile) => {\n if (inputFile.closest('.inner').querySelector('input[data-customization-text]'))\n inputFile.closest('.inner').querySelector('input[data-customization-text]').value = '';\n inputFile.addEventListener('change', (e) => {\n e.preventDefault();\n\n let display = inputFile.closest('.inner').querySelector('[data-customizations-display]');\n let inputText = inputFile.closest('.inner').querySelector('input[data-customization-text]');\n let messageWarning = inputFile.closest('.inner').querySelector('[data-customizations-message-warning]');\n\n if (e.target.files) handleFiles(inputText, [...e.target.files], messageWarning, null, display);\n });\n });\n\n // Lida com zona para arrastar arquivos\n dropZones.forEach((dropZone) => {\n dropZone.addEventListener('dragover', (e) => {\n e.preventDefault();\n });\n dropZone.addEventListener('drop', (e) => {\n e.preventDefault();\n\n let display = dropZone.closest('.inner').querySelector('[data-customizations-display]');\n let inputText = dropZone.closest('.inner').querySelector('input[data-customization-text]');\n let messageWarning = dropZone.closest('.inner').querySelector('[data-customizations-message-warning]');\n let messageError = dropZone.closest('.inner').querySelector('[data-customizations-message-error]');\n\n let files = e.dataTransfer.items\n ? [...e.dataTransfer.items].filter((item) => item.kind === 'file').map((item) => item.getAsFile())\n : e.dataTransfer.files\n ? [...e.dataTransfer.files]\n : [];\n\n if (files.length > 0) handleFiles(inputText, files, messageWarning, messageError, display);\n });\n });\n }\n },\n\n // Lida com personalização das faixas\n handleBandCustominization: function () {\n const custominizationBands = document.querySelectorAll('.customization-bands');\n\n if (custominizationBands.length > 0) {\n custominizationBands.forEach(custominizationBand => {\n const actionBand = custominizationBand.querySelector('.action-band');\n const colorInputs = custominizationBand.querySelectorAll('[data-color-band-sku] input');\n let buttonBand = custominizationBand.querySelectorAll('[data-button-band-sku]');\n\n colorInputs.forEach(input => {\n input.addEventListener('click', (e) => { \n e.preventDefault();\n\n setTimeout(() => {\n input.checked ? input.checked = false : input.checked = true;\n }, 5)\n })\n })\n \n if (buttonBand)\n buttonBand.forEach((button) => {\n button.addEventListener('click', () => {\n const activeColor = custominizationBand.querySelector('[data-color-band-sku].-show');\n const activeButton = custominizationBand.querySelector('[data-button-band-sku].-active');\n if (activeColor) {\n activeColor.classList.remove('-show')\n activeColor.querySelectorAll('input').forEach(input => input.disabled = true)\n };\n if (activeButton) activeButton.classList.remove('-active');\n \n const buttonSku = button.getAttribute('data-button-band-sku');\n const colorSku = custominizationBand.querySelector(`[data-color-band-sku='${buttonSku}']`);\n const topicTitle = custominizationBand.querySelector('.topic-title');\n \n button.classList.add('-active');\n topicTitle.classList.add('-show');\n colorSku.classList.add('-show');\n colorSku.querySelectorAll('input').forEach(input => input.disabled = false)\n\n });\n });\n })\n }\n },\n\n // Configura exibição do valor individual de cada personalização\n handleIndividualPrices: function () {\n const customs = document.querySelectorAll('[data-custom-option]');\n\n customs.forEach((custom) => {\n const value = custom.querySelector('[data-value]');\n const price = custom.querySelector('[data-customization-price]');\n\n if (price) {\n const valuePrice = price.getAttribute('data-customization-price');\n\n if (valuePrice && Number(valuePrice) > 0) {\n if (window.location.search.includes(`language=en`)) {\n let valueDolar = valuePrice.toLocaleString('en-US', { style: 'currency', currency: 'USD' });\n value.innerHTML = `(+ $${valueDolar})`;\n } else {\n value.innerHTML = `(+ ${formatMoney(Number(valuePrice))})`;\n }\n }\n }\n });\n },\n\n handleSelectCustomizations: function () {\n const custom = document.querySelector('.customization');\n\n if (custom) {\n const customOptions = custom.querySelectorAll('[data-custom-option]');\n const button = custom.querySelector('[data-button-custominization]');\n\n if (button)\n button.addEventListener('click', () => {\n custom.classList.toggle('-hidden');\n });\n\n if (customOptions.length > 0)\n customOptions.forEach((option) => {\n let buttonCheck = option.querySelector('[data-custominization-check]');\n\n //Habilita/Desabilita a personalização\n buttonCheck.addEventListener('click', () => {\n let optionsCheck = custom.querySelector('[data-custom-option].-show');\n\n if (optionsCheck) {\n optionsCheck.classList.remove('-show');\n\n // Desabilita os inputs do elemento que acabou de perder a classe '-show'\n const previousInputs = optionsCheck.querySelectorAll('[data-customization]');\n previousInputs.forEach((input) => {\n input.setAttribute('disabled', 'disabled');\n });\n }\n\n // Adiciona a classe '-show' ao elemento atual\n option.classList.add('-show');\n\n // Habilita os inputs do elemento que acabou de receber a classe '-show'\n const inputs = option.querySelectorAll('[data-customization]');\n inputs.forEach((input) => {\n input.removeAttribute('disabled');\n });\n });\n });\n }\n },\n\n setLetterCustominization: function () {\n const customLetter = document.querySelector('[data-custom-letter]');\n\n if (customLetter) {\n const input = customLetter.querySelector('[data-customization]');\n const topLetter = customLetter.querySelector('[data-top-letter]');\n const clean = customLetter.querySelector('[data-clean-letter]');\n const letters = customLetter.querySelectorAll('[data-letter-custominization]');\n let arrayLetters = [];\n\n //LIDA COM OS EVENTOS DAS LETRAS\n if (letters.length > 0)\n letters.forEach((letter) => {\n letter.addEventListener('change', (e) => {\n let value = letter.getAttribute('data-letter-custominization');\n let lettersCheckeds = customLetter.querySelectorAll('[data-letter-custominization]:checked');\n\n if (lettersCheckeds.length <= 3) {\n if (arrayLetters.length <= 2) arrayLetters.push(value);\n\n if (lettersCheckeds.length == 3) customLetter.classList.add('-block');\n } else {\n letter.checked = false;\n }\n\n input.value = '';\n topLetter.classList.add('-show');\n arrayLetters.forEach((arrayLetter, i) => {\n input.value = i == 0 ? arrayLetter : input.value + ` ${arrayLetter}`;\n });\n });\n });\n\n //LIDA COM O BOTÃO DE LIMPAR\n if (clean)\n clean.addEventListener('click', () => {\n input.value = '';\n arrayLetters = [];\n topLetter.classList.remove('-show');\n customLetter.classList.remove('-block');\n\n letters.forEach((letter) => {\n letter.checked = false;\n });\n });\n }\n },\n\n // Marca a primeira opção das customizações que possuem mais de uma opção\n markFirstOption: function () {\n const customs = document.querySelectorAll('[data-custom-option]');\n customs.forEach((custom) => {\n const inputs = custom.querySelectorAll('input[type=\"radio\"], input[type=\"checkbox\"]');\n if (inputs.length > 0) {\n const firstAvailable = custom.querySelector('.label.-available');\n if (firstAvailable) firstAvailable.click();\n }\n });\n },\n\n init: function () {\n this.handleBandCustominization();\n this.handleIndividualPrices();\n this.handleSelectCustomizations();\n this.setLetterCustominization();\n this.markFirstOption();\n\n const productBox = document.querySelector('[data-product-box]');\n this.customizationsFile(productBox);\n },\n};\n\nexport default Customizations;\n","const UploadImage = {\r\n ///////////////////////////////////////////\r\n //// Dados do Cloudinary\r\n ///////////////////////////////////////////\r\n config: {\r\n cloudName: 'dyr8ktx3p',\r\n uploadPreset: 'dyr8ktx3p',\r\n allowedTypes: [\r\n 'image/png',\r\n 'application/pdf',\r\n ]\r\n },\r\n ///////////////////////////////////////////\r\n //// Validação do tipo do arquivo\r\n ///////////////////////////////////////////\r\n validateFile: function (file) {\r\n if (!this.config.allowedTypes.includes(file[0].type)) {\r\n console.error('Formato não permitido do arquivo: ', file[0])\r\n return false\r\n }\r\n console.log('Formato do arquivo permitido: ', file[0])\r\n return true\r\n },\r\n\r\n upload: async function (file) {\r\n try {\r\n ///////////////////////////////////////////\r\n //// Implementação com Cloudinary\r\n ///////////////////////////////////////////\r\n\r\n const formData = new FormData();\r\n formData.append('file', file);\r\n formData.append('upload_preset', this.config.uploadPreset);\r\n formData.append('folder', 'user_uploads'); // Configurado no Cloudinary\r\n formData.append('tags', 'web_upload'); // Para melhor gerenciamento\r\n formData.append('context', `filename=${encodeURIComponent(file.name)}`);\r\n\r\n const response = await fetch(\r\n `https://api.cloudinary.com/v1_1/${this.config.cloudName}/image/upload`,\r\n {\r\n method: 'POST',\r\n body: formData,\r\n referrerPolicy: 'no-referrer' // Header de segurança\r\n }\r\n );\r\n\r\n if (!response.ok) {\r\n const error = await (response.json());\r\n throw new Error(error.message || 'Erro no upload');\r\n }\r\n\r\n const data = await response.json();\r\n\r\n console.log('link gerado: ', data.secure_url);\r\n\r\n return data.secure_url\r\n\r\n } catch (error) {\r\n console.error('Erro no upload: ', error);\r\n this.showError(error.message);\r\n }\r\n }\r\n}\r\n\r\nexport default UploadImage;\r\n","import CartDrawer from '../common/cartDrawer';\nimport Customizations from '../pages/product/customizations';\nimport UploadImage from './uploadImages';\nimport { urlencodeFormData } from './utilities';\n\nconst Store = {\n openCartDrawer: true,\n openCartDrawerMobile: true,\n\n addProduct: function(form, parent, callback) {\n // Se tiver customizações, ajusta campos de texto que não foram preenchidos\n const customizations = form.querySelectorAll('[data-customization]')\n const quantity = Number(form.querySelector('[name=\"quantity\"]').value)\n if (customizations.length > 0) customizations.forEach(custom => {\n const type = custom.getAttribute('type')\n\n if (type !== 'checkbox' || type !== 'radio') {\n if (custom.value === '') {\n custom.setAttribute('disabled', true)\n }\n }\n })\n\n if (customizations.length > 0 && quantity > 1) {\n // Item com personalização e com quantidade alta\n Store.addItemsCustom(form, parent, callback)\n } else {\n // Item com quantidade 1, com ou sem personalização\n Store.addItem(form, parent, callback)\n }\n },\n\n addItemsCustom: async function(form, parent, callback) {\n const _this = this;\n const btnComprar = parent.querySelector('[data-action=\"add-cart\"]');\n const boxResponse = parent.querySelector('[data-form-product] .msg-response:not(.resp-validate)');\n const quantity = Number(form.querySelector('[name=\"quantity\"]').value)\n let hasCustoms = false\n let customs = {}\n\n // Prepara o item e suas personalizações\n let data = { items: [\n {\n sku: form.querySelector('[name=\"sku\"]').value,\n quantity,\n customizations: []\n }\n ]}\n\n const customizations = form.querySelectorAll('[data-customization]')\n if (customizations.length > 0) customizations.forEach(custom => {\n const disabled = custom.getAttribute('disabled')\n const type = custom.getAttribute('type')\n let checked = true;\n if (type !== 'text' && type !== 'textarea') checked = custom.checked\n\n if (checked && disabled !== 'true') {\n hasCustoms = true\n if (type === 'text' || type === 'textarea') {\n customs[custom.getAttribute('data-customization-name')] = custom.value\n } else {\n customs[custom.getAttribute('data-customization-name')] = custom.getAttribute('value') \n }\n }\n })\n\n if (hasCustoms) {\n for (let index = 0; index < quantity; index++) {\n data.items[0].customizations.push(customs)\n }\n }\n\n const json_data = JSON.stringify(data)\n\n console.info('addItemsCustom');\n // console.log(json_data)\n\n // Envia o produto com suas personalizações\n if (!btnComprar.classList.contains('-adding')) {\n btnComprar.classList.add('-adding')\n\n try {\n const response = await fetch('/carrinho/adicionar/kit', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: json_data,\n });\n \n const html = await response.text();\n\n if (response.ok) {\n console.log('addItemsCustom - success');\n if (typeof callback == 'function') {\n callback('produto-adicionado', html, boxResponse, form);\n } else {\n _this.addItemResult('produto-adicionado', html, boxResponse, form);\n }\n\n } else {\n console.log('addItemsCustom - server não reconheceu adição');\n if (typeof callback == 'function') {\n callback('erro-adicionar', error, boxResponse, form);\n } else {\n _this.addItemResult('erro-adicionar', error, boxResponse, form);\n }\n }\n\n } catch (error) {\n console.log('addItemsCustom - error');\n console.error(error);\n\n if (typeof callback == 'function') {\n callback('erro-adicionar', error, boxResponse, form);\n } else {\n _this.addItemResult('erro-adicionar', error, boxResponse, form);\n }\n }\n\n customizations.forEach(custom => {\n custom.removeAttribute('disabled')\n })\n\n btnComprar.classList.remove('-adding');\n }\n },\n\n addItem: async function (form, parent, callback) {\n const _this = this;\n const btnComprar = parent.querySelector('[data-action=\"add-cart\"]');\n const urlAdd = '/carrinho/adicionar';\n const boxResponse = parent.querySelector('[data-form-product] .msg-response:not(.resp-validate)');\n\n // Itera os objetos com input e arquivos de Customizations e insere os links como value\n const fileInputs = Customizations.fileInputs;\n if (fileInputs.length > 0) {\n\n for (const fileInput of fileInputs) {\n\n if (fileInput.input && !fileInput.input.hasAttribute('disabled')) {\n const fileArray = await Promise.all(\n fileInput.files.length > 0 && fileInput.files.map(file => UploadImage.upload(file))\n )\n \n fileInput.input.value = [...fileArray].toString().replaceAll(',', ', ');\n }\n \n }\n\n }\n\n const formData = urlencodeFormData(new FormData(form));\n\n console.info('addItem');\n\n if (!btnComprar.classList.contains('-adding')) {\n btnComprar.classList.add('-adding');\n\n try {\n const response = await fetch(urlAdd, {\n method: 'POST',\n headers: {\n 'Accept': 'application/json, text/javascript, */*; q=0.0',\n 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',\n },\n body: formData,\n });\n\n const html = await response.text();\n\n console.log('addItem - success');\n\n if (typeof callback == 'function') {\n callback('produto-adicionado', html, boxResponse, form);\n } else {\n _this.addItemResult('produto-adicionado', html, boxResponse, form);\n }\n\n } catch (error) {\n console.log('addItem - error');\n console.error(error);\n\n if (typeof callback == 'function') {\n callback('erro-adicionar', error, boxResponse, form);\n } else {\n _this.addItemResult('erro-adicionar', error, boxResponse, form);\n }\n }\n\n btnComprar.classList.remove('-adding');\n }\n },\n\n deleteItem: async function (itemId, item, removeItemResult) {\n const _this = this;\n\n try {\n const body = new URLSearchParams({\n _method: 'DELETE',\n item_id: itemId\n });\n\n const response = await fetch('/carrinho', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',\n 'Accept': 'application/json, text/javascript, */*; q=0.01',\n },\n body\n })\n\n console.log('success');\n \n if (window.innerWidth > 768) {\n if (_this.openCartDrawer) {\n CartDrawer.updateCartCount();\n }\n } else {\n if (_this.openCartDrawerMobile) {\n CartDrawer.updateCartCount();\n }\n }\n\n if (typeof removeItemResult == 'function') {\n removeItemResult(item);\n }\n } catch (error) {\n console.log('Erro ao remover item do carrinho');\n console.log(error);\n }\n },\n\n validateFormProduct: function (form) {\n // VALIDA O FORM DE PRODUTO PARA VER SE O PRODUTO PODE SER ADICIONADO\n var btnComprar = form.querySelector('[data-action=\"add-cart\"]');\n var validated = true;\n var errors = [];\n\n if (btnComprar.dataset.available == 'false') {\n validated = false;\n errors.push('');\n } else {\n // verifica se o sku foi selecionado\n if (form.querySelector('[name=\"sku\"]').value == '') {\n validated = false;\n errors.push('Selecione um atributo para o produto');\n }\n\n // verifica se a quantidade é válida\n if (form.querySelector('input[name=\"quantity\"]').value <= 0) {\n validated = false;\n errors.push('Quantidade indisponível');\n }\n }\n\n return { validated, errors };\n },\n\n setRespValidateProduct: function (resp, form, boxResponse) {\n let htmlErrors = '';\n\n if (resp.validated) {\n boxResponse.innerHTML = '';\n } else {\n for (var i = resp.errors.length - 1; i >= 0; i--) {\n htmlErrors += '' + resp.errors[i] + '';\n }\n boxResponse.innerHTML = htmlErrors;\n }\n },\n\n addItemResult: function (typeResult, result, boxResponse, form) {\n const _this = this;\n\n if (typeResult == 'produto-adicionado') {\n const btnComprar = form.querySelector('[data-action=\"add-cart\"]');\n\n if (btnComprar) btnComprar.classList.add('success');\n\n setTimeout(function () {\n if (btnComprar) btnComprar.classList.remove('success');\n if (btnComprar) btnComprar.innerHTML = btnComprar.dataset.textAvailable;\n }, 3500);\n\n // CartDrawer.updateCartCount();\n\n if (window.innerWidth > 768) {\n if (_this.openCartDrawer) {\n CartDrawer.show();\n } else {\n setTimeout(() => {\n window.location.href = urlCart;\n }, 150);\n }\n } else {\n if (_this.openCartDrawerMobile) {\n CartDrawer.show();\n } else {\n setTimeout(() => {\n window.location.href = urlCart;\n }, 150);\n }\n }\n\n // Verifica se há popup de compra rápida ativo. Se tiver, fecha ele\n // Retirar ou comentar se não for utilizado\n const popupQuickview = document.querySelectorAll('[data-popup-quickview]');\n popupQuickview.length > 0 && popupQuickview.forEach(popup => {\n if (popup.classList.contains('-active')) {\n popup.querySelector('[data-close-popup-quickview]').click();\n }\n })\n\n // Remove disabled das personalizações de texto, se existirem\n const customizations = form.querySelectorAll('[data-customization]')\n if (customizations.length > 0) customizations.forEach(custom => {\n const type = custom.getAttribute('type')\n if (type !== 'checkbox' || type !== 'radio') {\n custom.removeAttribute('disabled')\n }\n })\n \n } else if (typeResult == 'erro-adicionar') {\n if (typeof boxResponse != 'undefined' && boxResponse.length > 0) {\n window.scrollTo({\n top: boxResponse.offsetTop,\n behavior: 'smooth',\n });\n\n boxResponse.classList.add('error');\n boxResponse.classList.remove('success');\n boxResponse.querySelector('span').innerHTML =\n 'Ocorreu um erro, tente novamente.';\n }\n }\n },\n};\n\nexport default Store;\n","import Store from './components/store';\r\nimport { formatMoney } from './components/utilities';\r\n\r\nconst GroupShop = {\r\n products: document.querySelectorAll('[data-prod-group-shop]'),\r\n selectedProducts: [],\r\n\r\n //Marca/desmarca o produto para adição\r\n handleSelection: function (currentProduct) {\r\n const { selectedProducts, addItemToBuy, removeItem } = GroupShop;\r\n\r\n if (selectedProducts.length > 0) {\r\n const match = selectedProducts.filter((product) => {\r\n return product.ref == currentProduct.getAttribute('data-prod-ref');\r\n });\r\n\r\n if (match.length == 0) {\r\n addItemToBuy(currentProduct);\r\n } else {\r\n removeItem(currentProduct);\r\n }\r\n } else {\r\n // Sem produto nenhum, adicionar normalmente o primeiro\r\n addItemToBuy(currentProduct);\r\n }\r\n },\r\n\r\n // Adiciona item na array para adição futura\r\n addItemToBuy: function (product) {\r\n const ref = product.getAttribute('data-prod-ref');\r\n\r\n const price = Number(product.getAttribute('data-price'));\r\n const salePrice = Number(product.getAttribute('data-sale-price'));\r\n const available = product.getAttribute('data-available');\r\n const discountPercent = product.getAttribute('data-discount-percent');\r\n\r\n // Muda sinalização do botão\r\n const button = product.querySelector('.add-to-group');\r\n if (button) button.classList.add('-group-selected');\r\n\r\n // Adiciona produto na array\r\n GroupShop.selectedProducts.push({\r\n productEl: product,\r\n ref,\r\n price,\r\n salePrice,\r\n available,\r\n discountPercent,\r\n });\r\n\r\n // Atualiza Preço\r\n GroupShop.updateFinalPrice();\r\n },\r\n\r\n // Remove item da array para adição futura\r\n removeItem: function (product) {\r\n for (let index = 0; index < GroupShop.selectedProducts.length; index++) {\r\n const selectedProduct = GroupShop.selectedProducts[index];\r\n\r\n if (selectedProduct.ref == product.getAttribute('data-prod-ref')) {\r\n // Muda sinalização do botão\r\n const button = product.querySelector('.add-to-group');\r\n if (button) button.classList.remove('-group-selected');\r\n\r\n // Remove produto da array de adição\r\n GroupShop.selectedProducts.splice(index, 1);\r\n break;\r\n }\r\n }\r\n\r\n // Atualiza Preço\r\n GroupShop.updateFinalPrice();\r\n },\r\n\r\n // Atualiza exibição de valores\r\n updateFinalPrice: function () {\r\n const { selectedProducts } = GroupShop;\r\n const purchaseWrapper = document.querySelector('.group-shop .purchase');\r\n\r\n if (selectedProducts.length == 0) {\r\n if (purchaseWrapper) purchaseWrapper.classList.add('-hidden');\r\n } else {\r\n document.querySelector('.group-shop .purchase').classList.remove('-hidden');\r\n\r\n let priceTotal = 0.0;\r\n let salePriceTotal = 0.0;\r\n let discountTotal = 0;\r\n let unavailableProducts = 0;\r\n\r\n // Soma os valores dos produtos selecionados, já considerando se tem desconto informado\r\n //e se a variante escolhida está disponível ou não\r\n\r\n selectedProducts.forEach((product) => {\r\n const prodPrice = parseFloat(product.productEl.getAttribute('data-price'));\r\n const salePrice = parseFloat(product.productEl.getAttribute('data-sale-price'));\r\n const available = product.productEl.getAttribute('data-available');\r\n\r\n if (available === 'false') {\r\n unavailableProducts++;\r\n } else {\r\n if (prodPrice != salePrice) {\r\n priceTotal = priceTotal + prodPrice;\r\n salePriceTotal = salePriceTotal + salePrice;\r\n } else {\r\n priceTotal = priceTotal + prodPrice;\r\n salePriceTotal = salePriceTotal + prodPrice;\r\n }\r\n }\r\n });\r\n\r\n if (priceTotal !== salePriceTotal) {\r\n discountTotal = 100 - (salePriceTotal * 100) / priceTotal;\r\n }\r\n\r\n // Exibe os valores finais calculados em tela\r\n const textEl = document.querySelector('.group-shop .purchase .text');\r\n const priceEl = document.querySelector('.group-shop .purchase .price-group');\r\n const totalProducts = selectedProducts.length - unavailableProducts;\r\n\r\n textEl.innerText = `Compre os ${totalProducts} produtos por`;\r\n priceEl.innerText = formatMoney(salePriceTotal);\r\n\r\n if (discountTotal != 0) {\r\n document.querySelector('.discount-wrapper').classList.remove('-hidden');\r\n\r\n const originalPriceEl = document.querySelector('.group-shop .purchase .original-price');\r\n const discountEl = document.querySelector('.group-shop .purchase .discount-percent');\r\n const warning = document.querySelector('.group-shop .purchase .warning');\r\n originalPriceEl.innerText = formatMoney(priceTotal);\r\n discountEl.innerText = `-${discountTotal.toFixed(2)}%`;\r\n\r\n if (unavailableProducts > 0) {\r\n warning.innerText = `Atenção! ${unavailableProducts} ${\r\n unavailableProducts > 1 ? 'produtos estão indisponíveis' : 'produto está indisponível'\r\n } `;\r\n } else {\r\n warning.innerText = '';\r\n }\r\n }\r\n }\r\n },\r\n\r\n // Adiciona os produtos selecionados e o produto principal\r\n addProducts: async function () {\r\n const btnComprar = document.querySelector('[data-group-shop-add]');\r\n const mainForm = document.querySelector('[data-form-product-group-shop]');\r\n\r\n let data = {\r\n items: [],\r\n };\r\n\r\n // Produtos selecionados no compre junto\r\n for (let index = 0; index < GroupShop.selectedProducts.length; index++) {\r\n const product = GroupShop.selectedProducts[index].productEl;\r\n const sku = product.querySelector('input[name=\"sku\"]').value;\r\n const available = product.getAttribute('data-available');\r\n let customizations = [];\r\n\r\n if (available === 'true') {\r\n const customsEl = product.querySelectorAll('[data-customization]');\r\n if (customsEl.length > 0) {\r\n let hasCustoms = false;\r\n let customs = {};\r\n\r\n customsEl.forEach((custom) => {\r\n // Desabilita as customs que não devem ser enviadas\r\n const type = custom.getAttribute('type');\r\n\r\n if (type !== 'checkbox' || type !== 'radio') {\r\n if (custom.value === '') {\r\n custom.setAttribute('disabled', true);\r\n }\r\n }\r\n\r\n // Prepara para adição as que não estão desabilitadas\r\n const disabled = custom.getAttribute('disabled');\r\n let checked = true;\r\n if (typeof custom.checked != 'undefined') checked = custom.checked;\r\n\r\n if (checked && disabled !== 'true') {\r\n hasCustoms = true;\r\n customs[custom.getAttribute('data-customization-name')] = custom.getAttribute('value');\r\n }\r\n });\r\n\r\n // Monta a array de customizations\r\n if (hasCustoms) {\r\n customizations.push(customs);\r\n }\r\n }\r\n\r\n // Salva o produto para adição\r\n data.items.push({\r\n sku: sku,\r\n quantity: 1,\r\n customizations,\r\n });\r\n }\r\n }\r\n\r\n const json_data = JSON.stringify(data);\r\n\r\n // Produto principal da página\r\n const purchaseWrapper = document.querySelector('.purchase');\r\n const boxResponse = purchaseWrapper.querySelector('.msg-response');\r\n\r\n if (!btnComprar.classList.contains('-adding')) {\r\n btnComprar.classList.add('-adding');\r\n\r\n try {\r\n const response = await fetch('/carrinho/adicionar/kit', {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n },\r\n body: json_data,\r\n });\r\n\r\n if (response.ok) {\r\n console.log('success');\r\n const html = await response.text();\r\n Store.addItemResult('produto-adicionado', html, boxResponse, mainForm);\r\n } else {\r\n console.log('error');\r\n console.error(`Erro ao adicionar os produtos do compre junto. Status ${response.status}`);\r\n Store.addItemResult('erro-adicionar', null, boxResponse, mainForm);\r\n }\r\n } catch (error) {\r\n console.error('Erro ao enviar os produtos do compre junto.');\r\n console.error(error);\r\n Store.addItemResult('erro-adicionar', null, boxResponse, mainForm);\r\n }\r\n\r\n btnComprar.classList.remove('-adding');\r\n }\r\n },\r\n\r\n setSlide: function () {\r\n const slider = new Swiper('[data-swiper-group]', {\r\n loop: false,\r\n slidesPerView: 1.1,\r\n spaceBetween: 4,\r\n watchOverflow: true,\r\n pagination: {\r\n el: '[data-swiper-group] .swiper-pagination',\r\n type: 'progressbar',\r\n },\r\n breakpoints: {\r\n 768: {\r\n slidesPerView: 1.6,\r\n spaceBetween: 4,\r\n },\r\n 992: {\r\n slidesPerView: 3,\r\n spaceBetween: 16,\r\n },\r\n },\r\n });\r\n },\r\n\r\n init: function () {\r\n const { products, handleSelection, addProducts } = GroupShop;\r\n\r\n if (products.length > 0) {\r\n this.setSlide();\r\n products.forEach((product) => {\r\n // Adiciona todos os produtos no compre junto no load da loja\r\n handleSelection(product);\r\n\r\n // Lida com o clique no botão de adicionar/remover produto do compre junto\r\n const button = product.querySelector('.add-to-group');\r\n if (button) {\r\n button.addEventListener('click', () => {\r\n handleSelection(product);\r\n });\r\n }\r\n\r\n // Lida com o evento de atualização de preço\r\n product.addEventListener('vnda:group-shop-price-update', () => {\r\n GroupShop.updateFinalPrice();\r\n });\r\n });\r\n\r\n // Lida com o botão de adicionar os produtos do compre junto no carrinho\r\n document.querySelector('[data-group-shop-add]').addEventListener('click', () => {\r\n addProducts();\r\n });\r\n }\r\n },\r\n};\r\n\r\nGroupShop.init();\r\n"],"names":[],"sourceRoot":""}