PageSourceSearch

https://cilia.com.br/scripts/pages/clubedeoficinas/script.js

js cilia.com.br collected 2026-10-01 14:19:18 UTC 77,939 bytes, 2,280 lines download raw bytes

1// ========== CONFIGURACAO DOS CUSTOM SELECTS (SINGLE) ==========
2const CUSTOM_SELECT_OPTIONS = {
3  uf: {
4    placeholder: "Selecione o estado",
5    searchable: true,
6    options: [
7      { value: "AC", label: "AC - Acre" },
8      { value: "AL", label: "AL - Alagoas" },
9      { value: "AP", label: "AP - Amapá" },
10      { value: "AM", label: "AM - Amazonas" },
11      { value: "BA", label: "BA - Bahia" },
12      { value: "CE", label: "CE - Ceará" },
13      { value: "DF", label: "DF - Distrito Federal" },
14      { value: "ES", label: "ES - Espírito Santo" },
15      { value: "GO", label: "GO - Goiás" },
16      { value: "MA", label: "MA - Maranhão" },
17      { value: "MT", label: "MT - Mato Grosso" },
18      { value: "MS", label: "MS - Mato Grosso do Sul" },
19      { value: "MG", label: "MG - Minas Gerais" },
20      { value: "PA", label: "PA - Pará" },
21      { value: "PB", label: "PB - Paraíba" },
22      { value: "PR", label: "PR - Paraná" },
23      { value: "PE", label: "PE - Pernambuco" },
24      { value: "PI", label: "PI - Piauí" },
25      { value: "RJ", label: "RJ - Rio de Janeiro" },
26      { value: "RN", label: "RN - Rio Grande do Norte" },
27      { value: "RS", label: "RS - Rio Grande do Sul" },
28      { value: "RO", label: "RO - Rondônia" },
29      { value: "RR", label: "RR - Roraima" },
30      { value: "SC", label: "SC - Santa Catarina" },
31      { value: "SP", label: "SP - São Paulo" },
32      { value: "SE", label: "SE - Sergipe" },
33      { value: "TO", label: "TO - Tocantins" },
34    ],
35  },
36  cargo: {
37    placeholder: "Selecione o cargo",
38    searchable: false,
39    options: [
40      { value: "proprietario", label: "Proprietário" },
41      { value: "gerente", label: "Gerente" },
42      { value: "orcamentista", label: "Orçamentista" },
43      { value: "funileiro", label: "Funileiro" },
44      { value: "preparador", label: "Preparador" },
45      { value: "pintor", label: "Pintor" },
46      { value: "colorista", label: "Colorista" },
47      { value: "montador", label: "Montador" },
48      { value: "mecanico", label: "Mecânico" },
49      { value: "polidor", label: "Polidor" },
50      { value: "estoquista", label: "Estoquista" },
51      { value: "recepcionista", label: "Recepcionista" },
52      { value: "administrativo", label: "Administrativo" },
53      { value: "financeiro", label: "Financeiro" },
54      { value: "outro", label: "Outro" },
55    ],
56  },
57  parentesco: {
58    placeholder: "Selecione",
59    searchable: false,
60    options: [
61      { value: "conjuge", label: "Cônjuge" },
62      { value: "filho", label: "Filho(a)" },
63      { value: "pai", label: "Pai" },
64      { value: "mae", label: "Mãe" },
65      { value: "outro", label: "Outro" },
66    ],
67  },
68};
69
70// ========== CONFIGURACAO DOS MULTISELECTS ==========
71const MULTISELECT_OPTIONS = {
72  "ms-sistema-orcamento": {
73    placeholder: "Selecione os sistemas",
74    allowCustom: true,
75    options: [
76      { value: "audatex", label: "Audatex" },
77      { value: "cilia", label: "Cilia", selected: true },
78      { value: "orion", label: "Orion" },
79      { value: "soma", label: "Soma" },
80    ],
81  },
82  "ms-sistema-gestao": {
83    placeholder: "Selecione os sistemas",
84    allowCustom: true,
85    options: [
86      // Sistemas especializados para oficinas
87      { value: "databox", label: "DataBox" },
88      { value: "ultracar", label: "UltraCar" },
89      { value: "oficina-inteligente", label: "Oficina Inteligente" },
90      { value: "oficina-integrada", label: "Oficina Integrada" },
91      { value: "sismecanica", label: "SisMecanica (LimerSoft)" },
92      { value: "onmotor", label: "OnMotor" },
93      { value: "motorsw", label: "Motor SW" },
94      { value: "hammer-system", label: "Hammer System" },
95      { value: "gestaoclick", label: "GestaoClick" },
96      { value: "mecanie", label: "Mecanie" },
97      { value: "bruning", label: "Bruning Sistemas" },
98      { value: "sige-cloud", label: "Sige Cloud" },
99      { value: "vhsys", label: "vhsys" },
100      { value: "empresarius", label: "Empresarius ERP" },
101      // ERPs gerais
102      { value: "totvs", label: "TOTVS" },
103      { value: "bling", label: "Bling" },
104      { value: "tiny", label: "Tiny ERP" },
105      { value: "omie", label: "Omie" },
106      { value: "conta-azul", label: "ContaAzul" },
107    ],
108  },
109  "ms-seguradoras": {
110    placeholder: "Selecione ou adicione seguradoras",
111    allowCustom: true,
112    options: [
113      { value: "nenhuma", label: "Nenhuma" },
114      // Principais seguradoras auto Brasil
115      { value: "porto-seguro", label: "Porto Seguro" },
116      { value: "azul", label: "Azul Seguros" },
117      { value: "bradesco", label: "Bradesco Seguros" },
118      { value: "sulamerica", label: "SulAmérica" },
119      { value: "itau", label: "Itaú Seguros" },
120      { value: "allianz", label: "Allianz" },
121      { value: "hdi", label: "HDI Seguros" },
122      { value: "tokio-marine", label: "Tokio Marine" },
123      { value: "mapfre", label: "Mapfre" },
124      { value: "liberty", label: "Liberty Seguros" },
125      { value: "sompo", label: "Sompo Seguros" },
126      { value: "suhai", label: "Suhai Seguradora" },
127      { value: "youse", label: "Youse" },
128      { value: "zurich", label: "Zurich" },
129      { value: "chubb", label: "Chubb" },
130      { value: "mitsui", label: "Mitsui Sumitomo" },
131      // Locadoras/Frotas
132      { value: "localiza", label: "Localiza (Frotas)" },
133      { value: "movida", label: "Movida (Frotas)" },
134      { value: "unidas", label: "Unidas (Frotas)" },
135    ],
136  },
137  "ms-plataformas-compra": {
138    placeholder: "Selecione ou adicione plataformas",
139    allowCustom: true,
140    options: [
141      // Plataformas especializadas autopecas
142      { value: "partsfy", label: "Partsfy" },
143      { value: "e-peca", label: "E-Peca" },
144      { value: "pts-parts", label: "PTS Parts" },
145      // Fabricantes/Distribuidores
146      { value: "nakata", label: "Nakata" },
147      { value: "cofap", label: "Cofap" },
148      { value: "sabo", label: "Sabo" },
149      { value: "bosch", label: "Bosch" },
150      { value: "mahle", label: "Mahle" },
151      { value: "ngk", label: "NGK" },
152      { value: "valeo", label: "Valeo" },
153      { value: "denso", label: "Denso" },
154      { value: "continental", label: "Continental" },
155      { value: "gates", label: "Gates" },
156      { value: "monroe", label: "Monroe" },
157      { value: "skf", label: "SKF" },
158      // Redes de distribuicao
159      { value: "dpaschoal", label: "DPaschoal" },
160      // Marketplaces gerais
161      { value: "mercado-livre", label: "Mercado Livre" },
162      { value: "amazon", label: "Amazon" },
163    ],
164  },
165  "ms-notas-fiscais": {
166    placeholder: "Selecione os tipos",
167    allowCustom: false,
168    options: [
169      { value: "nf-servicos", label: "NF de Serviços" },
170      { value: "nf-pecas-servicos", label: "NF de Peças e Serviços" },
171    ],
172  },
173  "ms-tipos-veiculos": {
174    placeholder: "Selecione os tipos",
175    allowCustom: false,
176    options: [
177      { value: "passeio", label: "Veículos de Passeio" },
178      { value: "motos", label: "Motos" },
179      { value: "utilitarios", label: "Veículos Utilitários" },
180      { value: "caminhoes", label: "Caminhões" },
181      { value: "onibus", label: "Ônibus" },
182    ],
183  },
184  "ms-marcas-veiculos": {
185    placeholder: "Selecione ou adicione marcas",
186    allowCustom: true,
187    options: [
188      // Marcas populares Brasil
189      { value: "volkswagen", label: "Volkswagen" },
190      { value: "fiat", label: "Fiat" },
191      { value: "chevrolet", label: "Chevrolet" },
192      { value: "ford", label: "Ford" },
193      { value: "toyota", label: "Toyota" },
194      { value: "hyundai", label: "Hyundai" },
195      { value: "honda", label: "Honda" },
196      { value: "renault", label: "Renault" },
197      { value: "nissan", label: "Nissan" },
198      { value: "jeep", label: "Jeep" },
199      { value: "peugeot", label: "Peugeot" },
200      { value: "citroen", label: "Citroën" },
201      { value: "kia", label: "Kia" },
202      { value: "mitsubishi", label: "Mitsubishi" },
203      { value: "suzuki", label: "Suzuki" },
204      { value: "subaru", label: "Subaru" },
205      // Marcas premium/luxo
206      { value: "bmw", label: "BMW" },
207      { value: "mercedes", label: "Mercedes-Benz" },
208      { value: "audi", label: "Audi" },
209      { value: "volvo", label: "Volvo" },
210      { value: "land-rover", label: "Land Rover" },
211      { value: "porsche", label: "Porsche" },
212      { value: "jaguar", label: "Jaguar" },
213      { value: "lexus", label: "Lexus" },
214      { value: "alfa-romeo", label: "Alfa Romeo" },
215      { value: "maserati", label: "Maserati" },
216      { value: "ferrari", label: "Ferrari" },
217      { value: "lamborghini", label: "Lamborghini" },
218      { value: "bentley", label: "Bentley" },
219      { value: "rolls-royce", label: "Rolls-Royce" },
220      { value: "aston-martin", label: "Aston Martin" },
221      { value: "mclaren", label: "McLaren" },
222      { value: "mini", label: "Mini" },
223      // Marcas americanas
224      { value: "ram", label: "RAM" },
225      { value: "dodge", label: "Dodge" },
226      { value: "chrysler", label: "Chrysler" },
227      { value: "tesla", label: "Tesla" },
228      // Marcas chinesas
229      { value: "caoa-chery", label: "Caoa Chery" },
230      { value: "byd", label: "BYD" },
231      { value: "gwm", label: "GWM" },
232      { value: "jac", label: "JAC Motors" },
233      { value: "lifan", label: "Lifan" },
234      { value: "haval", label: "Haval" },
235      { value: "shineray", label: "Shineray" },
236      // Marcas coreanas
237      { value: "ssangyong", label: "SsangYong" },
238      // Marcas caminhoes/comerciais
239      { value: "scania", label: "Scania" },
240      { value: "volvo-trucks", label: "Volvo Caminhões" },
241      { value: "mercedes-trucks", label: "Mercedes-Benz Caminhões" },
242      { value: "man", label: "MAN" },
243      { value: "daf", label: "DAF" },
244      { value: "iveco", label: "Iveco" },
245      { value: "vw-caminhoes", label: "VW Caminhões" },
246      { value: "ford-trucks", label: "Ford Caminhões" },
247      // Marcas de motos
248      { value: "honda-motos", label: "Honda Motos" },
249      { value: "yamaha", label: "Yamaha" },
250      { value: "suzuki-motos", label: "Suzuki Motos" },
251      { value: "kawasaki", label: "Kawasaki" },
252      { value: "bmw-motorrad", label: "BMW Motorrad" },
253      { value: "harley-davidson", label: "Harley-Davidson" },
254      { value: "ducati", label: "Ducati" },
255      { value: "triumph", label: "Triumph" },
256      { value: "royal-enfield", label: "Royal Enfield" },
257      { value: "ktm", label: "KTM" },
258      { value: "dafra", label: "Dafra" },
259      { value: "shineray-motos", label: "Shineray Motos" },
260      { value: "haojue", label: "Haojue" },
261      // Genéricos
262      { value: "multimarcas", label: "Multimarcas" },
263      { value: "outros", label: "Outros" },
264    ],
265  },
266  "ms-especializacoes": {
267    placeholder: "Selecione as especializações",
268    allowCustom: false,
269    options: [
270      { value: "blindados", label: "Blindados" },
271      { value: "funilaria-aluminio", label: "Funilaria de Alumínio" },
272      { value: "fibra-vidro", label: "Fibra de Vidro" },
273      { value: "veiculos-eletrificados", label: "Veículos Eletrificados" },
274      { value: "veiculos-premium", label: "Veículos Premium" },
275      { value: "martelinho-ouro", label: "Martelinho de Ouro" },
276      { value: "instalacao-vidros", label: "Instalação de Vidros" },
277      { value: "mecanica", label: "Mecânica" },
278      { value: "cambio-automatico", label: "Câmbio Automático" },
279      { value: "estetica-automotiva", label: "Estética Automotiva" },
280    ],
281  },
282  "ms-banheiros": {
283    placeholder: "Selecione os tipos",
284    allowCustom: false,
285    options: [
286      { value: "feminino", label: "Feminino" },
287      { value: "masculino", label: "Masculino" },
288      { value: "unissex", label: "Unissex" },
289    ],
290  },
291  "ms-tipos-solda": {
292    placeholder: "Selecione os tipos",
293    allowCustom: false,
294    options: [
295      { value: "mig-mag", label: "MIG/MAG" },
296      { value: "solda-ponto", label: "Solda Ponto" },
297      { value: "oxiacetileno", label: "Oxiacetileno" },
298      { value: "tig", label: "TIG" },
299      { value: "outros", label: "Outros" },
300    ],
301  },
302  "ms-tipo-lixamento": {
303    placeholder: "Selecione os tipos",
304    allowCustom: false,
305    options: [
306      { value: "umido", label: "Úmido" },
307      { value: "seco", label: "Seco" },
308    ],
309  },
310};
311
312// ========== ESTADO DA APLICACAO ==========
313let currentStep = 1;
314const totalSteps = 6;
315let colaboradorCount = 1;
316
317// Labels dos steps para exibição mobile
318const STEP_LABELS = {
319  1: "Dados da Oficina",
320  2: "Operação",
321  3: "Estrutura",
322  4: "Ferramental",
323  5: "Fotos",
324  6: "Saúde",
325};
326
327// ========== NAVEGACAO ENTRE STEPS ==========
328function goToStep(step) {
329  if (step < 1 || step > totalSteps) return;
330
331  // Valida step atual antes de avançar (não valida ao voltar)
332  if (step > currentStep) {
333    const errors = validateStep(currentStep);
334    if (errors.length > 0) {
335      showAlertModal({
336        type: "error",
337        title: "Campos obrigatórios",
338        message: "Preencha todos os campos obrigatórios para continuar.",
339        list: errors,
340      });
341      return;
342    }
343  }
344
345  // Remove active do step atual
346  const currentStepEl = document.getElementById(`step${currentStep}`);
347  if (currentStepEl) {
348    currentStepEl.classList.remove("active-step");
349  }
350
351  // Esconde conteudo atual
352  document
353    .getElementById(`step${currentStep}-content`)
354    .classList.remove("active");
355
356  // Marca steps anteriores como completed
357  for (let i = 1; i < step; i++) {
358    const prevStep = document.getElementById(`step${i}`);
359    if (prevStep && !prevStep.classList.contains("completed-step")) {
360      prevStep.classList.add("completed-step");
361      const stepNumber = prevStep.querySelector(".step-number");
362      if (stepNumber) {
363        stepNumber.innerHTML = '<i class="pi pi-check"></i>';
364      }
365    }
366  }
367
368  // Remove completed dos steps futuros (caso usuario volte)
369  for (let i = step; i <= totalSteps; i++) {
370    const futureStep = document.getElementById(`step${i}`);
371    if (futureStep) {
372      futureStep.classList.remove("completed-step");
373      const stepNumber = futureStep.querySelector(".step-number");
374      if (stepNumber && stepNumber.innerHTML.includes("pi-check")) {
375        stepNumber.innerHTML = i;
376      }
377    }
378  }
379
380  // Mostra novo step
381  currentStep = step;
382  const newStepEl = document.getElementById(`step${currentStep}`);
383  if (newStepEl) {
384    newStepEl.classList.add("active-step");
385    newStepEl.classList.remove("completed-step");
386    const stepNumber = newStepEl.querySelector(".step-number");
387    if (stepNumber) {
388      stepNumber.innerHTML = currentStep;
389    }
390  }
391
392  document.getElementById(`step${currentStep}-content`).classList.add("active");
393
394  // Atualiza progress bar animada
395  updateProgressBar();
396
397  // Atualiza info mobile
398  updateMobileProgressInfo();
399
400  // Salva lead parcial ao avançar de etapa
401  sendPartialLead();
402
403  // Scroll to top
404  window.scrollTo({ top: 0, behavior: "smooth" });
405}
406
407// Atualiza a barra de progresso animada
408function updateProgressBar() {
409  const progressBarFill = document.getElementById("progress-bar-fill");
410  if (progressBarFill) {
411    progressBarFill.setAttribute("data-step", currentStep);
412  }
413}
414
415function updateMobileProgressInfo() {
416  const mobileInfo = document.querySelector(".mobile-progress-info");
417  if (mobileInfo) {
418    mobileInfo.innerHTML = `
419      <span class="current-step">Etapa ${currentStep} de ${totalSteps}</span>
420      <span> — ${STEP_LABELS[currentStep]}</span>
421    `;
422  }
423}
424
425// ========== TOGGLE BUTTONS SIM/NAO ==========
426function toggleButton(btn, activeClass) {
427  const group = btn.parentElement;
428  group.querySelectorAll(".toggle-btn").forEach((b) => {
429    b.classList.remove("active", "active-green", "active-red");
430  });
431  btn.classList.add(activeClass);
432}
433
434// ========== SECOES COLAPSAVEIS ==========
435function toggleCollapsible(header) {
436  header.classList.toggle("open");
437  header.nextElementSibling.classList.toggle("open");
438}
439
440// ========== UPLOAD DE FOTOS ==========
441function markUploaded(input) {
442  if (input.files.length > 0) {
443    const item = input.closest(".photo-upload-item");
444    item.classList.add("has-file");
445    item.querySelector("i").className = "pi pi-check-circle";
446  }
447}
448
449// ========== COLABORADORES ==========
450function toggleColaborador(header) {
451  const card = header.closest(".colaborador-card");
452  card.classList.toggle("collapsed");
453}
454
455function addColaborador() {
456  // Colapsa todos os cards existentes
457  document.querySelectorAll("#colaboradores-container .colaborador-card").forEach((card) => {
458    card.classList.add("collapsed");
459  });
460  colaboradorCount++;
461  const container = document.getElementById("colaboradores-container");
462  const firstCard = container.querySelector(".colaborador-card");
463  const newCard = firstCard.cloneNode(true);
464
465  // Atualiza titulo
466  newCard.querySelector("h4").innerHTML =
467    `<i class="pi pi-user"></i> Colaborador ${colaboradorCount}`;
468
469  // Mostra botao de remover
470  newCard.querySelector(".btn-remove").style.display = "block";
471
472  // Limpa valores de inputs
473  newCard
474    .querySelectorAll('input:not([type="hidden"])')
475    .forEach((input) => (input.value = ""));
476
477  // Reseta custom-selects
478  newCard.querySelectorAll(".custom-select").forEach((cs) => {
479    const configKey = cs.dataset.config;
480    const config = CUSTOM_SELECT_OPTIONS[configKey];
481    if (config) {
482      const trigger = cs.querySelector(".custom-select-trigger");
483      trigger.innerHTML = `<span class="custom-select-placeholder">${config.placeholder}</span>`;
484      cs.querySelectorAll(".custom-select-option").forEach((opt) =>
485        opt.classList.remove("selected"),
486      );
487      cs.querySelector('input[type="hidden"]').value = "";
488    }
489  });
490
491  // Limpa dependentes exceto o primeiro (se existir)
492  const depList = newCard.querySelector(".dependentes-list");
493  if (depList) {
494    const depRows = depList.querySelectorAll(".dependente-row");
495    depRows.forEach((row, i) => {
496      if (i > 0) row.remove();
497      else {
498        row
499          .querySelectorAll('input:not([type="hidden"])')
500          .forEach((input) => (input.value = ""));
501        // Reseta custom-selects do dependente
502        row.querySelectorAll(".custom-select").forEach((cs) => {
503          const configKey = cs.dataset.config;
504          const config = CUSTOM_SELECT_OPTIONS[configKey];
505          if (config) {
506            const trigger = cs.querySelector(".custom-select-trigger");
507            trigger.innerHTML = `<span class="custom-select-placeholder">${config.placeholder}</span>`;
508            cs.querySelectorAll(".custom-select-option").forEach((opt) =>
509              opt.classList.remove("selected"),
510            );
511            cs.querySelector('input[type="hidden"]').value = "";
512          }
513        });
514      }
515    });
516  }
517
518  // Garante que o novo card está expandido
519  newCard.classList.remove("collapsed");
520
521  container.appendChild(newCard);
522}
523
524function removeColaborador(btn) {
525  btn.closest(".colaborador-card").remove();
526}
527
528// ========== DEPENDENTES ==========
529function addDependente(btn) {
530  const list = btn.previousElementSibling;
531  const firstRow = list.querySelector(".dependente-row");
532  const newRow = firstRow.cloneNode(true);
533
534  // Limpa inputs
535  newRow
536    .querySelectorAll('input:not([type="hidden"])')
537    .forEach((input) => (input.value = ""));
538
539  // Reseta custom-selects
540  newRow.querySelectorAll(".custom-select").forEach((cs) => {
541    const configKey = cs.dataset.config;
542    const config = CUSTOM_SELECT_OPTIONS[configKey];
543    if (config) {
544      const trigger = cs.querySelector(".custom-select-trigger");
545      trigger.innerHTML = `<span class="custom-select-placeholder">${config.placeholder}</span>`;
546      cs.querySelectorAll(".custom-select-option").forEach((opt) =>
547        opt.classList.remove("selected"),
548      );
549      cs.querySelector('input[type="hidden"]').value = "";
550    }
551  });
552
553  list.appendChild(newRow);
554}
555
556// ========== CONFIGURACAO DA API ==========
557const API_URL = "https://script.google.com/macros/s/AKfycbw-00hBrW4AeN302lhnn93WxsMo0kme1K-gcxuRcHL5v6bh4vz_VTWL0gXnNgzI2Ii6/exec";
558
559// Lead parcial: lê da tag script (injetado pelo Vue) ou usa Google Apps Script como fallback
560const _scriptTag = document.querySelector('script[src*="clubedeoficinas/script.js"]');
561const LEAD_API_URL = (_scriptTag && _scriptTag.dataset.apiUrl) || "";
562const LEAD_API_KEY = (_scriptTag && _scriptTag.dataset.apiKey) || "";
563
564// ========== ALERTAS (MODAL) ==========
565let lastFocusedElementBeforeAlertModal = null;
566let alertModalEscListenerBound = false;
567
568function bindAlertModalInteractions(overlay) {
569  if (!overlay || overlay.dataset.bound === "true") return;
570  overlay.dataset.bound = "true";
571
572  overlay.addEventListener("click", (e) => {
573    if (e.target === overlay) closeAlertModal();
574  });
575
576  if (!alertModalEscListenerBound) {
577    alertModalEscListenerBound = true;
578    document.addEventListener("keydown", (e) => {
579      const currentOverlay = document.getElementById("alert-modal");
580      if (
581        e.key === "Escape" &&
582        currentOverlay &&
583        currentOverlay.classList.contains("open")
584      ) {
585        closeAlertModal();
586      }
587    });
588  }
589}
590
591function ensureAlertModal() {
592  let overlay = document.getElementById("alert-modal");
593  if (!overlay) {
594    overlay = document.createElement("div");
595    overlay.className = "alert-modal-overlay";
596    overlay.id = "alert-modal";
597    overlay.setAttribute("aria-hidden", "true");
598    overlay.innerHTML = `
599      <div class="alert-modal alert-modal--info" role="dialog" aria-modal="true" aria-labelledby="alert-modal-title-text" aria-describedby="alert-modal-message">
600        <div class="alert-modal-header">
601          <h3 class="alert-modal-title">
602            <i class="pi pi-info-circle" id="alert-modal-icon"></i>
603            <span id="alert-modal-title-text">Aviso</span>
604          </h3>
605          <button type="button" class="alert-modal-close" aria-label="Fechar" onclick="closeAlertModal()">
606            <i class="pi pi-times"></i>
607          </button>
608        </div>
609        <div class="alert-modal-body">
610          <p class="alert-modal-message" id="alert-modal-message"></p>
611          <ul class="alert-modal-list" id="alert-modal-list" hidden></ul>
612        </div>
613        <div class="alert-modal-actions">
614          <button type="button" class="btn btn-primary" id="alert-modal-primary" onclick="closeAlertModal()">
615            Ok
616          </button>
617        </div>
618      </div>
619    `;
620    document.body.appendChild(overlay);
621  }
622
623  bindAlertModalInteractions(overlay);
624  return overlay;
625}
626
627function showAlertModal({
628  type = "info",
629  title = "Aviso",
630  message = "",
631  list,
632} = {}) {
633  const overlay = ensureAlertModal();
634
635  const modal = overlay.querySelector(".alert-modal");
636  const iconEl = document.getElementById("alert-modal-icon");
637  const titleEl = document.getElementById("alert-modal-title-text");
638  const messageEl = document.getElementById("alert-modal-message");
639  const listEl = document.getElementById("alert-modal-list");
640  const primaryBtn = document.getElementById("alert-modal-primary");
641
642  const normalizedType = ["info", "success", "error"].includes(type)
643    ? type
644    : "info";
645
646  if (modal) {
647    modal.classList.remove(
648      "alert-modal--info",
649      "alert-modal--success",
650      "alert-modal--error",
651    );
652    modal.classList.add(`alert-modal--${normalizedType}`);
653  }
654
655  if (iconEl) {
656    const iconByType = {
657      info: "pi-info-circle",
658      success: "pi-check-circle",
659      error: "pi-exclamation-triangle",
660    };
661    iconEl.className = `pi ${iconByType[normalizedType] || iconByType.info}`;
662  }
663
664  if (titleEl) titleEl.textContent = title || "Aviso";
665  if (messageEl) messageEl.textContent = message || "";
666
667  if (listEl) {
668    listEl.innerHTML = "";
669    const items = Array.isArray(list) ? list.filter(Boolean) : [];
670    items.forEach((item) => {
671      const li = document.createElement("li");
672      li.textContent = item;
673      listEl.appendChild(li);
674    });
675    listEl.hidden = items.length === 0;
676  }
677
678  lastFocusedElementBeforeAlertModal = document.activeElement;
679
680  overlay.classList.add("open");
681  overlay.setAttribute("aria-hidden", "false");
682  document.body.classList.add("alert-modal-open");
683
684  if (primaryBtn) primaryBtn.focus();
685}
686
687function closeAlertModal() {
688  const overlay = document.getElementById("alert-modal");
689  if (!overlay) return;
690
691  overlay.classList.remove("open");
692  overlay.setAttribute("aria-hidden", "true");
693  document.body.classList.remove("alert-modal-open");
694
695  const toFocus = lastFocusedElementBeforeAlertModal;
696  lastFocusedElementBeforeAlertModal = null;
697  if (toFocus && typeof toFocus.focus === "function") {
698    toFocus.focus();
699  }
700}
701
702// ========== SUBMIT FORM ==========
703async function submitForm() {
704  const submitBtn = document.querySelector(".btn-success");
705  const originalText = submitBtn.innerHTML;
706
707  try {
708    // Desabilita botao e mostra loading
709    submitBtn.disabled = true;
710    submitBtn.innerHTML = '<i class="pi pi-spin pi-spinner"></i> Enviando...';
711
712    // Coleta todos os dados
713    const formData = collectFormData();
714    console.log("Dados coletados:", formData);
715
716    // Validacao basica
717    const validation = validateFormData(formData);
718    if (!validation.valid) {
719      const requiredFields = (validation.errors || [])
720        .map((e) => e.replace(/^\s*-\s*/, "").trim())
721        .filter(Boolean);
722      showAlertModal({
723        type: "error",
724        title: "Campos obrigatórios",
725        message: "Por favor, preencha os campos obrigatórios para continuar.",
726        list: requiredFields,
727      });
728      submitBtn.disabled = false;
729      submitBtn.innerHTML = originalText;
730      return;
731    }
732
733    // Se nao tiver API configurada, apenas loga
734    if (!API_URL) {
735      console.log("API_URL nao configurada. Dados do formulario:", formData);
736      showAlertModal({
737        type: "info",
738        title: "Modo desenvolvimento",
739        message:
740          "API não configurada. Os dados foram registrados no console para teste.",
741      });
742      submitBtn.disabled = false;
743      submitBtn.innerHTML = originalText;
744      return;
745    }
746
747    // Fotos: converte para Base64 e envia para Google Drive
748    submitBtn.innerHTML =
749      '<i class="pi pi-spin pi-spinner"></i> Processando fotos...';
750    formData.fotos = await collectPhotosBase64();
751
752    submitBtn.innerHTML = '<i class="pi pi-spin pi-spinner"></i> Enviando...';
753
754    // Envia para Google Apps Script (planilha principal)
755    await fetch(API_URL, {
756      method: "POST",
757      mode: "no-cors",
758      headers: { "Content-Type": "application/json" },
759      body: JSON.stringify(formData),
760    });
761
762    // Com no-cors não conseguimos ler a resposta, mas se não der erro, funcionou
763    formSubmitted = true;
764    clearDraft();
765    window.location.assign("sucesso");
766
767    // Redireciona para página de sucesso ou início
768    // window.location.href = 'index.html';
769  } catch (error) {
770    console.error("Erro no envio:", error);
771    showAlertModal({
772      type: "error",
773      title: "Erro ao enviar cadastro",
774      message: "Não foi possível enviar. Por favor, tente novamente.",
775    });
776  } finally {
777    submitBtn.disabled = false;
778    submitBtn.innerHTML = originalText;
779  }
780}
781
782// ========== COLETA DE DADOS ==========
783function collectFormData() {
784  const data = {
785    dadosCadastrais: collectDadosCadastrais(),
786    operacao: collectOperacao(),
787    estrutura: collectEstrutura(),
788    ferramental: collectFerramental(),
789    fotos: {}, // Preenchido no upload
790    colaboradores: collectColaboradores(),
791  };
792
793  return data;
794}
795
796function collectDadosCadastrais() {
797  const logradouro = getInputValue("endereco");
798  const numero = getInputValue("endereco-numero");
799  const complemento = getInputValue("endereco-complemento");
800  const enderecoCompleto = [logradouro, numero, complemento]
801    .filter(Boolean)
802    .join(", ");
803
804  return {
805    razaoSocial: getInputValue("razao-social"),
806    cnpj: getInputValue("cnpj"),
807    nomeFantasia: getInputValue("nome-fantasia"),
808    inscricaoEstadual: getInputValue("inscricao-estadual"),
809    inscricaoMunicipal: getInputValue("inscricao-municipal"),
810    endereco: enderecoCompleto,
811    logradouro,
812    numero,
813    complemento,
814    cep: getInputValue("cep"),
815    bairro: getInputValue("bairro"),
816    cidade: getInputValue("cidade"),
817    uf: getSelectValue("uf-oficina"),
818    proprietario: getInputValue("proprietario"),
819    telefone: getInputValue("telefone"),
820    email: getInputValue("email"),
821    orcamentista: {
822      nome: getInputValue("orcamentista-nome"),
823      telefone: getInputValue("orcamentista-telefone"),
824      email: getInputValue("orcamentista-email"),
825    },
826    compradorPecas: {
827      nome: getInputValue("comprador-nome"),
828      telefone: getInputValue("comprador-telefone"),
829      email: getInputValue("comprador-email"),
830    },
831  };
832}
833
834function collectOperacao() {
835  return {
836    credenciadaSeguradoras: getToggleValue("credenciada-seguradoras"),
837    credenciadaAssociacoes: getToggleValue("credenciada-associacoes"),
838    seguradoras: getMultiselectValues("ms-seguradoras"),
839    passagemParticulares: parseInt(getInputValue("passagem-particulares")) || 0,
840    passagemCredenciadas: parseInt(getInputValue("passagem-credenciadas")) || 0,
841    possuiSistemaOrcamento: getToggleValue("possui-sistema-orcamento"),
842    sistemasOrcamento: getMultiselectValues("ms-sistema-orcamento"),
843    possuiSistemaGestao: getToggleValue("possui-sistema-gestao"),
844    sistemasGestao: getMultiselectValues("ms-sistema-gestao"),
845    utilizaPlataformaCompra: getToggleValue("utiliza-plataforma-compra"),
846    plataformasCompra: getMultiselectValues("ms-plataformas-compra"),
847    aceitaFornecimentoPecas: getToggleValue("aceita-fornecimento-pecas"),
848    aceitaCartoes: getToggleValue("aceita-cartoes"),
849    parcelaFranquia: getToggleValue("parcela-franquia"),
850    quantasParcelas: parseInt(getInputValue("quantas-parcelas")) || 0,
851    utilizaAntecipacao: getToggleValue("utiliza-antecipacao"),
852    tiposNotaFiscal: getMultiselectValues("ms-notas-fiscais"),
853    recebe24horas: getToggleValue("recebe-24horas"),
854    aceitaPlacaClube: getToggleValue("aceita-placa-clube"),
855    tiposVeiculos: getMultiselectValues("ms-tipos-veiculos"),
856    marcasVeiculos: getMultiselectValues("ms-marcas-veiculos"),
857    especializacoes: getMultiselectValues("ms-especializacoes"),
858  };
859}
860
861function collectEstrutura() {
862  return {
863    profissionais: collectProfissionais(),
864    suporte: collectSuporte(),
865    infraestrutura: {
866      possuiRecepcao: getToggleValue("possui-recepcao"),
867      banheiros: getMultiselectValues("ms-banheiros"),
868      localPecas: getToggleValue("possui-local-pecas"),
869      localSucatas: getToggleValue("possui-local-sucatas"),
870      localResiduos: getToggleValue("possui-local-residuos"),
871    },
872  };
873}
874
875function collectProfissionais() {
876  const profissionais = {};
877  const rows = document.querySelectorAll(
878    "#step3-content .professionals-table:first-of-type tbody tr",
879  );
880  const funcoes = [
881    "orcamentista",
882    "compradorPecas",
883    "funileiro",
884    "preparador",
885    "pintor",
886    "colorista",
887    "montador",
888    "estoquista",
889    "mecanico",
890    "polidor",
891    "martelinho",
892  ];
893
894  rows.forEach((row, index) => {
895    if (funcoes[index]) {
896      const inputs = row.querySelectorAll('input[type="number"]');
897      profissionais[funcoes[index]] = {
898        qtdProprio: parseInt(inputs[0]?.value) || 0,
899        qtdTerceirizado: parseInt(inputs[1]?.value) || 0,
900      };
901    }
902  });
903
904  return profissionais;
905}
906
907function collectSuporte() {
908  const suporte = {};
909  const rows = document.querySelectorAll(
910    "#step3-content .professionals-table:last-of-type tbody tr",
911  );
912  const funcoes = ["recepcao", "financeiro", "administrativo"];
913
914  rows.forEach((row, index) => {
915    if (funcoes[index]) {
916      const inputs = row.querySelectorAll('input[type="number"]');
917      suporte[funcoes[index]] = {
918        qtdProprio: parseInt(inputs[0]?.value) || 0,
919        qtdTerceirizado: parseInt(inputs[1]?.value) || 0,
920      };
921    }
922  });
923
924  return suporte;
925}
926
927function collectFerramental() {
928  const hasScanner = getToggleValue("scanner");
929
930  return {
931    funilaria: {
932      mesaEstiramento: getToggleValue("mesa-estiramento"),
933      ciborg: getToggleValue("ciborg"),
934      spotter: getToggleValue("spotter"),
935      tiposSolda: getMultiselectValues("ms-tipos-solda"),
936    },
937    pintura: {
938      mixerTintas: getToggleValue("mixer-tintas"),
939      baseAgua: getToggleValue("base-agua"),
940      marcasTinta: getInputValue("marcas-tinta"),
941      tipoLixamento: getMultiselectValues("ms-tipo-lixamento"),
942      planoAspirante: getToggleValue("plano-aspirante"),
943      cabinePintura: getToggleValue("cabine-pintura"),
944      cabineAquecimento: getToggleValue("cabine-aquecimento"),
945    },
946    diagnostico: {
947      scanner: hasScanner,
948      marcaScanner: hasScanner ? getInputValue("scanner-marca") : "",
949      modeloScanner: hasScanner ? getInputValue("scanner-modelo") : "",
950    },
951    reparacaoEspecial: {
952      ferramentasPlastico: getToggleValue("ferramentas-plastico"),
953      marcaPlastico: getInputValue("marca-plastico"),
954      ferramentasAluminio: getToggleValue("ferramentas-aluminio"),
955      marcaAluminio: getInputValue("marca-aluminio"),
956    },
957  };
958}
959
960function collectColaboradores() {
961  const colaboradores = [];
962  const cards = document.querySelectorAll(".colaborador-card");
963
964  cards.forEach((card) => {
965    const fields = card.querySelectorAll(".form-grid .field");
966    const getInput = (idx) =>
967      fields[idx]?.querySelector('input:not([type="hidden"])')?.value?.trim() ||
968      "";
969    const getSelect = (idx) =>
970      getCustomSelectValue(fields[idx]?.querySelector(".custom-select")) ||
971      fields[idx]?.querySelector("select")?.value ||
972      "";
973    const colaborador = {
974      nome: getInput(0),
975      cargo: getSelect(1),
976      cpf: getInput(2),
977      dataNascimento: getInput(3),
978      endereco: getInput(4),
979      bairro: getInput(5),
980      cidade: getInput(6),
981      uf: getSelect(7),
982      cep: getInput(8),
983      email: getInput(9),
984      telefone: getInput(10),
985      dependentes: collectDependentes(card),
986    };
987    colaboradores.push(colaborador);
988  });
989
990  return colaboradores;
991}
992
993function collectDependentes(card) {
994  const dependentes = [];
995  const rows = card.querySelectorAll(".dependente-row");
996
997  rows.forEach((row) => {
998    const inputs = row.querySelectorAll('input:not([type="hidden"])');
999    const parentesco =
1000      getCustomSelectValue(
1001        row.querySelector('.custom-select[data-config="parentesco"]'),
1002      ) ||
1003      row.querySelector("select")?.value ||
1004      "";
1005
1006    if (inputs[0]?.value) {
1007      // So adiciona se tiver nome
1008      dependentes.push({
1009        nome: inputs[0]?.value || "",
1010        parentesco: parentesco,
1011        cpf: inputs[1]?.value || "",
1012        dataNascimento: inputs[2]?.value || "",
1013      });
1014    }
1015  });
1016
1017  return dependentes;
1018}
1019
1020// ========== HELPERS DE COLETA ==========
1021function getInputValue(id) {
1022  const el = document.getElementById(id);
1023  return el ? el.value.trim() : "";
1024}
1025
1026function setInputValue(id, value) {
1027  const el = document.getElementById(id);
1028  if (!el) return;
1029  el.value = value || "";
1030}
1031
1032function getSelectValue(id) {
1033  const el = document.getElementById(id);
1034  if (!el) return "";
1035  // Verifica se foi convertido para custom-select
1036  const customSelect = el.closest ? el.closest(".custom-select") : null;
1037  if (customSelect) {
1038    return getCustomSelectValue(customSelect);
1039  }
1040  return el.value || "";
1041}
1042
1043function setSelectValue(id, value) {
1044  const el = document.getElementById(id);
1045  if (!el) return;
1046  // Verifica se foi convertido para custom-select
1047  const customSelect = el.closest ? el.closest(".custom-select") : null;
1048  if (customSelect) {
1049    setCustomSelectValue(customSelect, value);
1050    return;
1051  }
1052  el.value = value || "";
1053}
1054
1055function getCustomSelectValue(container) {
1056  if (!container) return "";
1057  const hidden = container.querySelector('input[type="hidden"]');
1058  return hidden ? hidden.value : "";
1059}
1060
1061function setCustomSelectValue(container, value) {
1062  if (!container) return;
1063
1064  const normalizedValue = (value || "").toString().trim().toUpperCase();
1065  const optionEl = container.querySelector(
1066    `.custom-select-option[data-value="${normalizedValue}"]`,
1067  );
1068  if (!optionEl) return;
1069
1070  container.querySelectorAll(".custom-select-option").forEach((opt) => {
1071    opt.classList.remove("selected");
1072  });
1073  optionEl.classList.add("selected");
1074
1075  const trigger = container.querySelector(".custom-select-trigger");
1076  if (trigger) {
1077    trigger.innerHTML = `<span class="custom-select-value">${optionEl.textContent.trim()}</span>`;
1078  }
1079
1080  const hidden = container.querySelector('input[type="hidden"]');
1081  if (hidden) hidden.value = normalizedValue;
1082}
1083
1084function getToggleValue(dataToggle) {
1085  const group = document.querySelector(`[data-toggle="${dataToggle}"]`);
1086  if (!group) return null;
1087  const activeBtn = group.querySelector(".toggle-btn.active-green");
1088  return activeBtn !== null;
1089}
1090
1091function getMultiselectValues(id) {
1092  const container = document.getElementById(id);
1093  if (!container) return [];
1094
1095  const selectedOptions = container.querySelectorAll(
1096    ".multiselect-option.selected",
1097  );
1098  return Array.from(selectedOptions).map((opt) => ({
1099    value: opt.dataset.value,
1100    label: opt.querySelector("span")?.textContent || "",
1101    isCustom: opt.dataset.custom === "true",
1102  }));
1103}
1104
1105function setToggleValue(dataToggle, value) {
1106  const group = document.querySelector(`[data-toggle="${dataToggle}"]`);
1107  if (!group || value === null || value === undefined) return;
1108  const btns = group.querySelectorAll(".toggle-btn");
1109  btns.forEach((b) => b.classList.remove("active", "active-green", "active-red"));
1110  if (btns.length >= 2) {
1111    if (value === true) btns[0].classList.add("active-green");
1112    else btns[1].classList.add("active-red");
1113  }
1114}
1115
1116function setMultiselectValues(id, values) {
1117  const container = document.getElementById(id);
1118  if (!container || !Array.isArray(values)) return;
1119  container.querySelectorAll(".multiselect-option").forEach((opt) => opt.classList.remove("selected"));
1120  const valSet = new Set(values.map((v) => (typeof v === "string" ? v : v.value)));
1121  values.forEach((v) => {
1122    const val = typeof v === "string" ? v : v.value;
1123    const label = typeof v === "string" ? v : v.label || v.value;
1124    const isCustom = typeof v === "object" && v.isCustom;
1125    if (isCustom && !container.querySelector(`.multiselect-option[data-value="${val}"]`)) {
1126      const opts = container.querySelector(".multiselect-options");
1127      if (opts) {
1128        const el = document.createElement("div");
1129        el.className = "multiselect-option";
1130        el.dataset.value = val;
1131        el.dataset.custom = "true";
1132        el.onclick = function () { selectOption(id, this); };
1133        el.innerHTML = `<div class="multiselect-option-checkbox"></div><span>${label}</span>`;
1134        opts.appendChild(el);
1135        valSet.add(val);
1136      }
1137    }
1138  });
1139  container.querySelectorAll(".multiselect-option").forEach((opt) => {
1140    if (valSet.has(opt.dataset.value)) opt.classList.add("selected");
1141  });
1142  updateMultiselectTrigger(id);
1143}
1144
1145// ========== FOTOS (V2 - conversão para Base64 e upload para Google Drive) ==========
1146
1147// Converte um arquivo para Base64
1148function fileToBase64(file) {
1149  return new Promise((resolve, reject) => {
1150    const reader = new FileReader();
1151    reader.onload = () => resolve(reader.result);
1152    reader.onerror = (error) => reject(error);
1153    reader.readAsDataURL(file);
1154  });
1155}
1156
1157// Coleta todas as fotos e converte para Base64
1158async function collectPhotosBase64() {
1159  const photoInputs = document.querySelectorAll(
1160    '#step5-content input[type="file"]',
1161  );
1162  const photos = {};
1163
1164  for (const input of photoInputs) {
1165    const field = input.dataset.field;
1166    if (field && input.files.length > 0) {
1167      const file = input.files[0];
1168      try {
1169        const base64 = await fileToBase64(file);
1170        photos[field] = {
1171          base64: base64,
1172          filename: file.name,
1173          mimeType: file.type || "image/jpeg",
1174        };
1175      } catch (error) {
1176        console.error(`Erro ao converter foto ${field}:`, error);
1177      }
1178    }
1179  }
1180
1181  return photos;
1182}
1183
1184// ========== VALIDACAO ==========
1185
1186function requireToggle(dataToggle, label) {
1187  const val = getToggleValue(dataToggle);
1188  return val === null ? label : null;
1189}
1190
1191function requireInput(id, label) {
1192  return getInputValue(id) ? null : label;
1193}
1194
1195function requireSelect(id, label) {
1196  return getSelectValue(id) ? null : label;
1197}
1198
1199function requireMultiselect(id, label) {
1200  return getMultiselectValues(id).length > 0 ? null : label;
1201}
1202
1203function validateStep(step) {
1204  const e = [];
1205  const push = (v) => { if (v) e.push(v); };
1206
1207  if (step === 1) {
1208    push(requireInput("cnpj", "CNPJ"));
1209    push(requireInput("razao-social", "Razão Social"));
1210    push(requireInput("nome-fantasia", "Nome Fantasia"));
1211    push(requireInput("inscricao-estadual", "Inscrição Estadual"));
1212    push(requireInput("inscricao-municipal", "Inscrição Municipal"));
1213    push(requireInput("cep", "CEP"));
1214    push(requireSelect("uf-oficina", "UF"));
1215    push(requireInput("endereco", "Endereço"));
1216    push(requireInput("endereco-numero", "Número"));
1217    push(requireInput("bairro", "Bairro"));
1218    push(requireInput("cidade", "Cidade"));
1219    push(requireInput("proprietario", "Nome do Proprietário"));
1220    push(requireInput("telefone", "Telefone"));
1221    push(requireInput("email", "E-mail"));
1222    push(requireInput("orcamentista-nome", "Orçamentista - Nome"));
1223    push(requireInput("orcamentista-telefone", "Orçamentista - Telefone"));
1224    push(requireInput("orcamentista-email", "Orçamentista - E-mail"));
1225    push(requireInput("comprador-nome", "Comprador - Nome"));
1226    push(requireInput("comprador-telefone", "Comprador - Telefone"));
1227    push(requireInput("comprador-email", "Comprador - E-mail"));
1228    // Validações de formato
1229    const cnpj = getInputValue("cnpj");
1230    if (cnpj && cnpj.replace(/\D/g, "").length !== 14) e.push("CNPJ inválido");
1231    const email = getInputValue("email");
1232    if (email && !email.includes("@")) e.push("E-mail inválido");
1233  }
1234
1235  if (step === 2) {
1236    push(requireToggle("credenciada-seguradoras", "Credenciada em Seguradoras"));
1237    push(requireToggle("credenciada-associacoes", "Credenciada em Associações"));
1238    push(requireMultiselect("ms-seguradoras", "Empresas credenciadas"));
1239    push(requireInput("passagem-particulares", "Passagem veículos (Particulares)"));
1240    push(requireInput("passagem-credenciadas", "Passagem veículos (Credenciadas)"));
1241    push(requireToggle("possui-sistema-orcamento", "Possui Sistema de Orçamentação"));
1242    push(requireMultiselect("ms-sistema-orcamento", "Qual sistema de orçamentação"));
1243    push(requireToggle("possui-sistema-gestao", "Possui Sistema de Gestão"));
1244    if (getToggleValue("possui-sistema-gestao") === true) {
1245      push(requireMultiselect("ms-sistema-gestao", "Qual sistema de gestão"));
1246    }
1247    push(requireToggle("utiliza-plataforma-compra", "Utiliza plataforma de compra"));
1248    if (getToggleValue("utiliza-plataforma-compra") === true) {
1249      push(requireMultiselect("ms-plataformas-compra", "Quais plataformas de compra"));
1250    }
1251    push(requireToggle("aceita-fornecimento-pecas", "Aceita fornecimento de peças"));
1252    push(requireToggle("aceita-cartoes", "Aceita Cartões"));
1253    push(requireToggle("parcela-franquia", "Parcela a franquia"));
1254    if (getToggleValue("parcela-franquia") === true) {
1255      push(requireInput("quantas-parcelas", "Quantas parcelas"));
1256    }
1257    push(requireToggle("utiliza-antecipacao", "Utiliza antecipação de crédito"));
1258    push(requireMultiselect("ms-notas-fiscais", "Emite Notas Fiscais"));
1259    push(requireToggle("recebe-24horas", "Recebe 24 horas"));
1260    push(requireToggle("aceita-placa-clube", "Aceita placa do Clube"));
1261    push(requireMultiselect("ms-tipos-veiculos", "Tipos de veículos"));
1262    push(requireMultiselect("ms-marcas-veiculos", "Marcas de veículos"));
1263    push(requireMultiselect("ms-especializacoes", "Especializações"));
1264  }
1265
1266  if (step === 3) {
1267    push(requireToggle("possui-recepcao", "Possui recepção e sala de espera"));
1268    push(requireMultiselect("ms-banheiros", "Banheiros disponíveis"));
1269    push(requireToggle("possui-local-pecas", "Possui local para peças"));
1270    push(requireToggle("possui-local-sucatas", "Possui local para sucatas"));
1271    push(requireToggle("possui-local-residuos", "Possui local para resíduos"));
1272  }
1273
1274  if (step === 4) {
1275    push(requireToggle("mesa-estiramento", "Possui mesa de estiramento"));
1276    push(requireToggle("ciborg", "Possui Ciborg"));
1277    push(requireToggle("spotter", "Possui Spotter"));
1278    push(requireMultiselect("ms-tipos-solda", "Tipos de Solda"));
1279    push(requireToggle("mixer-tintas", "Possui Mixer de Tintas"));
1280    push(requireToggle("base-agua", "Utiliza Base d'água"));
1281    push(requireInput("marcas-tinta", "Marcas de Tinta"));
1282    push(requireMultiselect("ms-tipo-lixamento", "Tipo de Lixamento"));
1283    push(requireToggle("plano-aspirante", "Possui Plano Aspirante"));
1284    push(requireToggle("cabine-pintura", "Possui Cabine de Pintura"));
1285    push(requireToggle("cabine-aquecimento", "Cabine com Aquecimento"));
1286    push(requireToggle("scanner", "Possui Scanner"));
1287    if (getToggleValue("scanner") === true) {
1288      push(requireInput("scanner-marca", "Marca do scanner"));
1289      push(requireInput("scanner-modelo", "Modelo do scanner"));
1290    }
1291    push(requireToggle("ferramentas-plastico", "Ferramentas para plásticos"));
1292    if (getToggleValue("ferramentas-plastico") === true) {
1293      push(requireInput("marca-plastico", "Marca ferramentas plásticos"));
1294    }
1295    push(requireToggle("ferramentas-aluminio", "Ferramentas para alumínio"));
1296    if (getToggleValue("ferramentas-aluminio") === true) {
1297      push(requireInput("marca-aluminio", "Marca ferramentas alumínio"));
1298    }
1299  }
1300
1301  // Step 5 (Fotos) - sem validação obrigatória
1302  // Step 6 validado no submitForm()
1303
1304  return e;
1305}
1306
1307function validateFormData(data) {
1308  const errors = [];
1309
1310  // Valida todos os steps de 1 a 5
1311  for (let s = 1; s <= 5; s++) {
1312    validateStep(s).forEach((err) => errors.push(err));
1313  }
1314
1315  // Step 6 - Colaboradores
1316  const cards = document.querySelectorAll(".colaborador-card");
1317  cards.forEach((card, idx) => {
1318    const prefix = cards.length > 1 ? `Colaborador ${idx + 1} - ` : "";
1319    const fields = card.querySelectorAll(".form-grid .field");
1320    const getVal = (i) => fields[i]?.querySelector('input:not([type="hidden"])')?.value?.trim() || "";
1321    const getSelVal = (i) => getCustomSelectValue(fields[i]?.querySelector(".custom-select")) || "";
1322
1323    if (!getVal(0)) errors.push(`${prefix}Nome Completo`);
1324    if (!getSelVal(1)) errors.push(`${prefix}Cargo`);
1325    if (!getVal(2)) errors.push(`${prefix}CPF`);
1326    if (!getVal(3)) errors.push(`${prefix}Data de Nascimento`);
1327    if (!getVal(4)) errors.push(`${prefix}Endereço`);
1328    if (!getVal(5)) errors.push(`${prefix}Bairro`);
1329    if (!getVal(6)) errors.push(`${prefix}Cidade`);
1330    if (!getSelVal(7)) errors.push(`${prefix}UF`);
1331    if (!getVal(8)) errors.push(`${prefix}CEP`);
1332    if (!getVal(9)) errors.push(`${prefix}E-mail`);
1333    if (!getVal(10)) errors.push(`${prefix}Telefone`);
1334  });
1335
1336  // Checkboxes
1337  if (!document.getElementById("checkbox-veracidade")?.checked)
1338    errors.push("Declaração de veracidade");
1339  if (!document.getElementById("checkbox-optin")?.checked)
1340    errors.push("Aceite da Política de Privacidade");
1341
1342  return { valid: errors.length === 0, errors };
1343}
1344
1345// ========== MASCARAS ==========
1346function formatCNPJ(input) {
1347  let value = input.value.replace(/\D/g, "");
1348  value = value.replace(/^(\d{2})(\d)/, "$1.$2");
1349  value = value.replace(/^(\d{2})\.(\d{3})(\d)/, "$1.$2.$3");
1350  value = value.replace(/\.(\d{3})(\d)/, ".$1/$2");
1351  value = value.replace(/(\d{4})(\d)/, "$1-$2");
1352  input.value = value;
1353}
1354
1355function formatCEP(input) {
1356  let value = input.value.replace(/\D/g, "");
1357  value = value.replace(/^(\d{5})(\d)/, "$1-$2");
1358  input.value = value;
1359}
1360
1361// ========== BUSCA DE CNPJ ==========
1362let cnpjLookupTimeout = null;
1363let lastCnpjLookup = "";
1364let cnpjAbortController = null;
1365let cnpjStatusTimeout = null;
1366
1367function setCnpjLookupStatus(status) {
1368  const el = document.getElementById("cnpj-lookup-status");
1369  if (!el) return;
1370
1371  clearTimeout(cnpjStatusTimeout);
1372  el.classList.remove("loading", "success", "error");
1373
1374  if (!status || status === "idle") {
1375    el.textContent = "";
1376    return;
1377  }
1378
1379  if (status === "loading") {
1380    el.classList.add("loading");
1381    el.textContent = "— Buscando dados...";
1382    return;
1383  }
1384
1385  if (status === "success") {
1386    el.classList.add("success");
1387    el.textContent = "— Dados preenchidos";
1388    cnpjStatusTimeout = setTimeout(() => setCnpjLookupStatus("idle"), 4000);
1389    return;
1390  }
1391
1392  el.classList.add("error");
1393  el.textContent = "— CNPJ não encontrado";
1394  cnpjStatusTimeout = setTimeout(() => setCnpjLookupStatus("idle"), 6000);
1395}
1396
1397function scheduleCnpjLookup(rawCnpj, immediate = false) {
1398  const cleanCnpj = (rawCnpj || "").replace(/\D/g, "").slice(0, 14);
1399  if (cleanCnpj.length !== 14) return;
1400  if (cleanCnpj === lastCnpjLookup) return;
1401
1402  clearTimeout(cnpjLookupTimeout);
1403
1404  if (immediate) {
1405    lookupCnpj(cleanCnpj);
1406    return;
1407  }
1408
1409  cnpjLookupTimeout = setTimeout(() => lookupCnpj(cleanCnpj), 300);
1410}
1411
1412function escapeRegExp(value) {
1413  return (value || "").replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1414}
1415
1416function buildLogradouroFromCnpj(data) {
1417  const tipo = (data?.descricao_tipo_de_logradouro || "").trim();
1418  const logradouro = (data?.logradouro || "").trim();
1419  const numero = (data?.numero || "").trim();
1420
1421  let endereco = logradouro;
1422  if (tipo && logradouro) {
1423    const enderecoLower = logradouro.toLowerCase();
1424    const tipoLower = tipo.toLowerCase();
1425    if (!enderecoLower.startsWith(`${tipoLower} `)) {
1426      endereco = `${tipo} ${logradouro}`.trim();
1427    }
1428  }
1429
1430  if (numero) {
1431    endereco = endereco
1432      .replace(new RegExp(`\\s*[,-]?\\s*${escapeRegExp(numero)}\\s*$`), "")
1433      .trim();
1434  }
1435
1436  return endereco;
1437}
1438
1439function applyCnpjDataToForm(data) {
1440  if (!data) return;
1441
1442  if (data.razao_social) setInputValue("razao-social", data.razao_social);
1443  if (data.nome_fantasia) setInputValue("nome-fantasia", data.nome_fantasia);
1444
1445  if (data.cep) {
1446    setInputValue("cep", data.cep);
1447    const cepEl = document.getElementById("cep");
1448    if (cepEl) formatCEP(cepEl);
1449  }
1450
1451  if (data.uf) setSelectValue("uf-oficina", data.uf);
1452  if (data.municipio) setInputValue("cidade", data.municipio);
1453  if (data.bairro) setInputValue("bairro", data.bairro);
1454
1455  const logradouro = buildLogradouroFromCnpj(data);
1456  if (logradouro) setInputValue("endereco", logradouro);
1457  if (data.numero) setInputValue("endereco-numero", data.numero);
1458  if (data.complemento) setInputValue("endereco-complemento", data.complemento);
1459
1460  if (!getInputValue("telefone") && data.ddd_telefone_1) {
1461    setInputValue("telefone", data.ddd_telefone_1);
1462    const telefoneEl = document.getElementById("telefone");
1463    if (telefoneEl) formatPhone(telefoneEl);
1464  }
1465
1466  if (!getInputValue("email") && data.email) {
1467    setInputValue("email", data.email);
1468  }
1469}
1470
1471async function lookupCnpj(cleanCnpj) {
1472  if (cnpjAbortController) cnpjAbortController.abort();
1473  cnpjAbortController = new AbortController();
1474
1475  setCnpjLookupStatus("loading");
1476
1477  try {
1478    const response = await fetch(
1479      `https://brasilapi.com.br/api/cnpj/v1/${cleanCnpj}`,
1480      { signal: cnpjAbortController.signal },
1481    );
1482    if (!response.ok) {
1483      setCnpjLookupStatus("error");
1484      return;
1485    }
1486
1487    const data = await response.json();
1488    if (!data?.cnpj) {
1489      setCnpjLookupStatus("error");
1490      return;
1491    }
1492
1493    lastCnpjLookup = cleanCnpj;
1494    applyCnpjDataToForm(data);
1495    setCnpjLookupStatus("success");
1496  } catch (error) {
1497    if (error?.name === "AbortError") return;
1498    console.warn("Erro ao buscar CNPJ:", error);
1499    setCnpjLookupStatus("error");
1500  }
1501}
1502
1503// ========== BUSCA DE CEP ==========
1504let cepLookupTimeout = null;
1505let lastCepLookup = "";
1506let cepAbortController = null;
1507
1508function scheduleCepLookup(rawCep, immediate = false) {
1509  const cleanCep = (rawCep || "").replace(/\D/g, "").slice(0, 8);
1510  if (cleanCep.length !== 8) return;
1511  if (cleanCep === lastCepLookup) return;
1512
1513  clearTimeout(cepLookupTimeout);
1514
1515  if (immediate) {
1516    lookupCep(cleanCep);
1517    return;
1518  }
1519
1520  cepLookupTimeout = setTimeout(() => lookupCep(cleanCep), 300);
1521}
1522
1523async function lookupCep(cleanCep) {
1524  if (cepAbortController) cepAbortController.abort();
1525  cepAbortController = new AbortController();
1526
1527  try {
1528    const response = await fetch(`https://viacep.com.br/ws/${cleanCep}/json/`, {
1529      signal: cepAbortController.signal,
1530    });
1531    if (!response.ok) return;
1532
1533    const data = await response.json();
1534    if (!data || data.erro) return;
1535
1536    lastCepLookup = cleanCep;
1537
1538    if (data.logradouro) setInputValue("endereco", data.logradouro);
1539    if (data.bairro) setInputValue("bairro", data.bairro);
1540    if (data.localidade) setInputValue("cidade", data.localidade);
1541    if (data.uf) setSelectValue("uf-oficina", data.uf);
1542  } catch (error) {
1543    if (error?.name === "AbortError") return;
1544    console.warn("Erro ao buscar CEP:", error);
1545  }
1546}
1547
1548function formatPhone(input) {
1549  const digits = (input.value || "").replace(/\D/g, "").slice(0, 11);
1550
1551  if (digits.length <= 2) {
1552    input.value = digits;
1553    return;
1554  }
1555
1556  const ddd = digits.slice(0, 2);
1557  const rest = digits.slice(2);
1558
1559  if (rest.length <= 4) {
1560    input.value = `(${ddd}) ${rest}`;
1561    return;
1562  }
1563
1564  if (rest.length <= 8) {
1565    input.value = `(${ddd}) ${rest.slice(0, 4)}-${rest.slice(4)}`;
1566    return;
1567  }
1568
1569  input.value = `(${ddd}) ${rest.slice(0, 5)}-${rest.slice(5)}`;
1570}
1571
1572function formatCPF(input) {
1573  let value = input.value.replace(/\D/g, "");
1574  value = value.replace(/(\d{3})(\d)/, "$1.$2");
1575  value = value.replace(/(\d{3})(\d)/, "$1.$2");
1576  value = value.replace(/(\d{3})(\d{1,2})$/, "$1-$2");
1577  input.value = value;
1578}
1579
1580// ========== MULTISELECT COMPONENT ==========
1581
1582// Inicializa um multiselect com base na configuracao
1583function initMultiselect(id) {
1584  const config = MULTISELECT_OPTIONS[id];
1585  if (!config) return;
1586
1587  const container = document.getElementById(id);
1588  if (!container) return;
1589
1590  // Define atributo de custom
1591  if (config.allowCustom) {
1592    container.dataset.allowCustom = "true";
1593  }
1594
1595  // Gera HTML do multiselect
1596  container.innerHTML = `
1597    <div class="multiselect-trigger" onclick="toggleMultiselect('${id}')">
1598      <span class="multiselect-placeholder">${config.placeholder}</span>
1599    </div>
1600    <div class="multiselect-dropdown">
1601      <div class="multiselect-search">
1602        <input type="text" placeholder="Buscar..." oninput="filterMultiselect('${id}', this.value)">
1603      </div>
1604      <div class="multiselect-options">
1605        ${config.options
1606          .map(
1607            (opt) => `
1608          <div class="multiselect-option${opt.selected ? " selected" : ""}"
1609               data-value="${opt.value}"
1610               onclick="selectOption('${id}', this)">
1611            <div class="multiselect-option-checkbox"></div>
1612            <span>${opt.label}</span>
1613          </div>
1614        `,
1615          )
1616          .join("")}
1617      </div>
1618      ${
1619        config.allowCustom
1620          ? `
1621        <div class="multiselect-add-custom hidden" onclick="addCustomOption('${id}')">
1622          <i class="pi pi-plus"></i>
1623          <span>Adicionar "<span class="custom-value"></span>"</span>
1624        </div>
1625      `
1626          : ""
1627      }
1628    </div>
1629  `;
1630
1631  // Atualiza trigger se houver opcoes pre-selecionadas
1632  updateMultiselectTrigger(id);
1633}
1634
1635// Toggle dropdown do multiselect
1636function toggleMultiselect(id) {
1637  const multiselect = document.getElementById(id);
1638  const dropdown = multiselect.querySelector(".multiselect-dropdown");
1639  const trigger = multiselect.querySelector(".multiselect-trigger");
1640  const isOpen = dropdown.classList.contains("open");
1641
1642  // Fecha todos os outros dropdowns
1643  document.querySelectorAll(".multiselect-dropdown.open").forEach((d) => {
1644    d.classList.remove("open");
1645    d.closest(".multiselect")
1646      .querySelector(".multiselect-trigger")
1647      .classList.remove("active");
1648  });
1649
1650  if (!isOpen) {
1651    dropdown.classList.add("open");
1652    trigger.classList.add("active");
1653    const searchInput = dropdown.querySelector(".multiselect-search input");
1654    if (searchInput) {
1655      searchInput.value = "";
1656      searchInput.focus();
1657      filterMultiselect(id, "");
1658    }
1659  }
1660}
1661
1662// Seleciona/deseleciona uma opcao
1663function selectOption(id, optionEl) {
1664  optionEl.classList.toggle("selected");
1665  updateMultiselectTrigger(id);
1666  event.stopPropagation();
1667}
1668
1669// Atualiza o trigger com os chips selecionados
1670function updateMultiselectTrigger(id) {
1671  const multiselect = document.getElementById(id);
1672  if (!multiselect) return;
1673
1674  const trigger = multiselect.querySelector(".multiselect-trigger");
1675  if (!trigger) return;
1676
1677  const selectedOptions = multiselect.querySelectorAll(
1678    ".multiselect-option.selected",
1679  );
1680  let placeholder = trigger.querySelector(".multiselect-placeholder");
1681
1682  // Remove chips existentes
1683  trigger
1684    .querySelectorAll(".multiselect-chip")
1685    .forEach((chip) => chip.remove());
1686
1687  if (selectedOptions.length === 0) {
1688    if (!placeholder) {
1689      const config = MULTISELECT_OPTIONS[id];
1690      const span = document.createElement("span");
1691      span.className = "multiselect-placeholder";
1692      span.textContent = config ? config.placeholder : "Selecione";
1693      trigger.insertBefore(span, trigger.firstChild);
1694    } else {
1695      placeholder.style.display = "inline";
1696    }
1697  } else {
1698    if (placeholder) placeholder.style.display = "none";
1699
1700    selectedOptions.forEach((option) => {
1701      const chip = document.createElement("div");
1702      chip.className = "multiselect-chip";
1703      if (option.dataset.custom === "true") {
1704        chip.classList.add("custom");
1705      }
1706      chip.innerHTML = `
1707        <span>${option.querySelector("span").textContent}</span>
1708        <i class="pi pi-times multiselect-chip-remove" onclick="removeChip('${id}', '${option.dataset.value}', event)"></i>
1709      `;
1710      trigger.insertBefore(chip, trigger.lastChild);
1711    });
1712  }
1713}
1714
1715// Remove um chip
1716function removeChip(id, value, event) {
1717  event.stopPropagation();
1718  const multiselect = document.getElementById(id);
1719  const option = multiselect.querySelector(
1720    `.multiselect-option[data-value="${value}"]`,
1721  );
1722  if (option) {
1723    option.classList.remove("selected");
1724    if (option.dataset.custom === "true") {
1725      option.remove();
1726    }
1727  }
1728  updateMultiselectTrigger(id);
1729}
1730
1731// Filtra opcoes do multiselect
1732function filterMultiselect(id, searchTerm) {
1733  const multiselect = document.getElementById(id);
1734  const options = multiselect.querySelectorAll(".multiselect-option");
1735  const addCustomBtn = multiselect.querySelector(".multiselect-add-custom");
1736  const allowCustom = multiselect.dataset.allowCustom === "true";
1737  const normalizedSearch = searchTerm.toLowerCase().trim();
1738
1739  let hasVisibleOption = false;
1740  let hasExactMatch = false;
1741
1742  options.forEach((option) => {
1743    const label = option.querySelector("span").textContent.toLowerCase();
1744    const matches = label.includes(normalizedSearch);
1745
1746    if (matches) {
1747      option.classList.remove("hidden");
1748      hasVisibleOption = true;
1749      if (label === normalizedSearch) {
1750        hasExactMatch = true;
1751      }
1752    } else {
1753      option.classList.add("hidden");
1754    }
1755  });
1756
1757  // Mostra/esconde botao de adicionar customizado
1758  if (allowCustom && addCustomBtn) {
1759    if (normalizedSearch && !hasExactMatch) {
1760      addCustomBtn.classList.remove("hidden");
1761      addCustomBtn.querySelector(".custom-value").textContent = searchTerm;
1762    } else {
1763      addCustomBtn.classList.add("hidden");
1764    }
1765  }
1766}
1767
1768// Adiciona uma opcao customizada
1769function addCustomOption(id) {
1770  const multiselect = document.getElementById(id);
1771  const searchInput = multiselect.querySelector(".multiselect-search input");
1772  const optionsContainer = multiselect.querySelector(".multiselect-options");
1773  const addCustomBtn = multiselect.querySelector(".multiselect-add-custom");
1774  const customValue = searchInput.value.trim();
1775
1776  if (!customValue) return;
1777
1778  // Cria nova opcao
1779  const newOption = document.createElement("div");
1780  const valueSlug = customValue
1781    .toLowerCase()
1782    .replace(/\s+/g, "-")
1783    .replace(/[^a-z0-9-]/g, "");
1784  newOption.className = "multiselect-option selected";
1785  newOption.dataset.value = "custom-" + valueSlug + "-" + Date.now();
1786  newOption.dataset.custom = "true";
1787  newOption.onclick = function () {
1788    selectOption(id, this);
1789  };
1790  newOption.innerHTML = `
1791    <div class="multiselect-option-checkbox"></div>
1792    <span>${customValue}</span>
1793  `;
1794
1795  optionsContainer.appendChild(newOption);
1796
1797  // Limpa busca e atualiza
1798  searchInput.value = "";
1799  filterMultiselect(id, "");
1800  addCustomBtn.classList.add("hidden");
1801  updateMultiselectTrigger(id);
1802}
1803
1804// ========== CUSTOM SELECT (SINGLE) COMPONENT ==========
1805
1806// Inicializa um custom select
1807function initCustomSelect(element, configKey) {
1808  const config = CUSTOM_SELECT_OPTIONS[configKey];
1809  if (!config) return;
1810
1811  // Cria o container
1812  const container = document.createElement("div");
1813  container.className = "custom-select";
1814  container.dataset.config = configKey;
1815  if (element.id) container.id = element.id;
1816
1817  // Pega o valor atual do select nativo se houver
1818  let currentValue = element.value;
1819  let currentLabel = "";
1820  if (currentValue) {
1821    const opt = config.options.find((o) => o.value === currentValue);
1822    if (opt) currentLabel = opt.label;
1823  }
1824
1825  // Gera HTML
1826  container.innerHTML = `
1827    <div class="custom-select-trigger" onclick="toggleCustomSelect(this.parentElement)">
1828      ${currentValue ? `<span class="custom-select-value">${currentLabel}</span>` : `<span class="custom-select-placeholder">${config.placeholder}</span>`}
1829    </div>
1830    <div class="custom-select-dropdown">
1831      ${
1832        config.searchable
1833          ? `
1834        <div class="custom-select-search">
1835          <input type="text" placeholder="Buscar..." oninput="filterCustomSelect(this)">
1836        </div>
1837      `
1838          : ""
1839      }
1840      <div class="custom-select-options">
1841        ${config.options
1842          .map(
1843            (opt) => `
1844          <div class="custom-select-option${opt.value === currentValue ? " selected" : ""}"
1845               data-value="${opt.value}"
1846               onclick="selectCustomOption(this)">
1847            ${opt.label}
1848          </div>
1849        `,
1850          )
1851          .join("")}
1852      </div>
1853    </div>
1854    <input type="hidden" name="${element.name || ""}" value="${currentValue || ""}">
1855  `;
1856
1857  // Substitui o select nativo
1858  element.replaceWith(container);
1859  return container;
1860}
1861
1862// Toggle dropdown do custom select
1863function toggleCustomSelect(container) {
1864  const dropdown = container.querySelector(".custom-select-dropdown");
1865  const trigger = container.querySelector(".custom-select-trigger");
1866  const isOpen = dropdown.classList.contains("open");
1867
1868  // Fecha todos os outros dropdowns (multiselect e custom-select)
1869  document
1870    .querySelectorAll(
1871      ".multiselect-dropdown.open, .custom-select-dropdown.open",
1872    )
1873    .forEach((d) => {
1874      d.classList.remove("open");
1875      const parentTrigger = d.parentElement.querySelector(
1876        ".multiselect-trigger, .custom-select-trigger",
1877      );
1878      if (parentTrigger) parentTrigger.classList.remove("active");
1879    });
1880
1881  if (!isOpen) {
1882    dropdown.classList.add("open");
1883    trigger.classList.add("active");
1884    const searchInput = dropdown.querySelector(".custom-select-search input");
1885    if (searchInput) {
1886      searchInput.value = "";
1887      searchInput.focus();
1888      filterCustomSelect(searchInput);
1889    }
1890  }
1891}
1892
1893// Seleciona uma opção
1894function selectCustomOption(optionEl) {
1895  const container = optionEl.closest(".custom-select");
1896  const dropdown = container.querySelector(".custom-select-dropdown");
1897  const trigger = container.querySelector(".custom-select-trigger");
1898  const hiddenInput = container.querySelector('input[type="hidden"]');
1899
1900  // Remove selected de outras opções
1901  container.querySelectorAll(".custom-select-option").forEach((opt) => {
1902    opt.classList.remove("selected");
1903  });
1904
1905  // Marca esta como selecionada
1906  optionEl.classList.add("selected");
1907
1908  // Atualiza o trigger
1909  trigger.innerHTML = `<span class="custom-select-value">${optionEl.textContent.trim()}</span>`;
1910
1911  // Atualiza o input hidden
1912  if (hiddenInput) {
1913    hiddenInput.value = optionEl.dataset.value;
1914  }
1915
1916  // Fecha o dropdown
1917  dropdown.classList.remove("open");
1918  trigger.classList.remove("active");
1919
1920  event.stopPropagation();
1921}
1922
1923// Filtra opções do custom select
1924function filterCustomSelect(searchInput) {
1925  const container = searchInput.closest(".custom-select");
1926  const options = container.querySelectorAll(".custom-select-option");
1927  const searchTerm = searchInput.value.toLowerCase().trim();
1928
1929  options.forEach((option) => {
1930    const label = option.textContent.toLowerCase();
1931    if (label.includes(searchTerm)) {
1932      option.classList.remove("hidden");
1933    } else {
1934      option.classList.add("hidden");
1935    }
1936  });
1937}
1938
1939// Inicializa todos os selects nativos que devem ser convertidos
1940function initAllCustomSelects() {
1941  // Selects de UF
1942  document.querySelectorAll("select.input-select").forEach((select) => {
1943    const label =
1944      select
1945        .closest(".field")
1946        ?.querySelector("label")
1947        ?.textContent?.toLowerCase() || "";
1948    const options = Array.from(select.options);
1949
1950    // Detecta o tipo de select pelo label ou opções
1951    if (
1952      label.includes("uf") ||
1953      label.includes("estado") ||
1954      (options.length > 2 &&
1955        options.some((o) => o.value === "SP" || o.text === "SP"))
1956    ) {
1957      initCustomSelect(select, "uf");
1958    } else if (label.includes("cargo")) {
1959      initCustomSelect(select, "cargo");
1960    } else if (label.includes("parentesco")) {
1961      initCustomSelect(select, "parentesco");
1962    }
1963  });
1964}
1965
1966// ========== RASCUNHO (AUTO-SAVE + RESTORE) ==========
1967const DRAFT_PREFIX = "cilia_oficina_draft_";
1968const LEAD_PARCIAL_URL = "https://script.google.com/macros/s/AKfycbwYov4PINuqz0RKkCpJ-kdMVOCCpBiVxgGQAgQRNnLVc-z2BPQoAI3jw1-XRdi7tsoaQA/exec";
1969
1970let autoSaveTimeout = null;
1971let formSubmitted = false;
1972
1973function getDraftKey() {
1974  const cnpj = (getInputValue("cnpj") || "").replace(/\D/g, "");
1975  return cnpj.length === 14 ? DRAFT_PREFIX + cnpj : null;
1976}
1977
1978function collectDraftData() {
1979  // Coleta tudo exceto fotos (muito grandes para localStorage)
1980  return {
1981    currentStep,
1982    timestamp: new Date().toISOString(),
1983    dadosCadastrais: collectDadosCadastrais(),
1984    operacao: collectOperacao(),
1985    estrutura: collectEstrutura(),
1986    ferramental: collectFerramental(),
1987    colaboradores: collectColaboradores(),
1988  };
1989}
1990
1991function scheduleDraftSave() {
1992  clearTimeout(autoSaveTimeout);
1993  autoSaveTimeout = setTimeout(saveDraft, 1000);
1994}
1995
1996function saveDraft() {
1997  const key = getDraftKey();
1998  if (!key) return;
1999  try {
2000    localStorage.setItem(key, JSON.stringify(collectDraftData()));
2001  } catch (e) {
2002    console.warn("Erro ao salvar rascunho:", e);
2003  }
2004}
2005
2006function loadDraft(cnpj) {
2007  const clean = (cnpj || "").replace(/\D/g, "");
2008  if (clean.length !== 14) return null;
2009  try {
2010    const raw = localStorage.getItem(DRAFT_PREFIX + clean);
2011    return raw ? JSON.parse(raw) : null;
2012  } catch (e) {
2013    return null;
2014  }
2015}
2016
2017function clearDraft() {
2018  const key = getDraftKey();
2019  if (key) localStorage.removeItem(key);
2020}
2021
2022function restoreDraft(data) {
2023  if (!data) return;
2024  const d = data.dadosCadastrais;
2025  if (d) {
2026    // Não sobrescreve CNPJ (já está preenchido)
2027    setInputValue("razao-social", d.razaoSocial);
2028    setInputValue("nome-fantasia", d.nomeFantasia);
2029    setInputValue("inscricao-estadual", d.inscricaoEstadual);
2030    setInputValue("inscricao-municipal", d.inscricaoMunicipal);
2031    setInputValue("cep", d.cep);
2032    setSelectValue("uf-oficina", d.uf);
2033    setInputValue("endereco", d.logradouro);
2034    setInputValue("endereco-numero", d.numero);
2035    setInputValue("endereco-complemento", d.complemento);
2036    setInputValue("bairro", d.bairro);
2037    setInputValue("cidade", d.cidade);
2038    setInputValue("proprietario", d.proprietario);
2039    setInputValue("telefone", d.telefone);
2040    setInputValue("email", d.email);
2041    if (d.orcamentista) {
2042      setInputValue("orcamentista-nome", d.orcamentista.nome);
2043      setInputValue("orcamentista-telefone", d.orcamentista.telefone);
2044      setInputValue("orcamentista-email", d.orcamentista.email);
2045    }
2046    if (d.compradorPecas) {
2047      setInputValue("comprador-nome", d.compradorPecas.nome);
2048      setInputValue("comprador-telefone", d.compradorPecas.telefone);
2049      setInputValue("comprador-email", d.compradorPecas.email);
2050    }
2051  }
2052
2053  const o = data.operacao;
2054  if (o) {
2055    setToggleValue("credenciada-seguradoras", o.credenciadaSeguradoras);
2056    setToggleValue("credenciada-associacoes", o.credenciadaAssociacoes);
2057    setMultiselectValues("ms-seguradoras", o.seguradoras || []);
2058    setInputValue("passagem-particulares", o.passagemParticulares || "");
2059    setInputValue("passagem-credenciadas", o.passagemCredenciadas || "");
2060    setToggleValue("possui-sistema-orcamento", o.possuiSistemaOrcamento);
2061    setMultiselectValues("ms-sistema-orcamento", o.sistemasOrcamento || []);
2062    setToggleValue("possui-sistema-gestao", o.possuiSistemaGestao);
2063    setMultiselectValues("ms-sistema-gestao", o.sistemasGestao || []);
2064    setToggleValue("utiliza-plataforma-compra", o.utilizaPlataformaCompra);
2065    setMultiselectValues("ms-plataformas-compra", o.plataformasCompra || []);
2066    setToggleValue("aceita-fornecimento-pecas", o.aceitaFornecimentoPecas);
2067    setToggleValue("aceita-cartoes", o.aceitaCartoes);
2068    setToggleValue("parcela-franquia", o.parcelaFranquia);
2069    setInputValue("quantas-parcelas", o.quantasParcelas || "");
2070    setToggleValue("utiliza-antecipacao", o.utilizaAntecipacao);
2071    setMultiselectValues("ms-notas-fiscais", o.tiposNotaFiscal || []);
2072    setToggleValue("recebe-24horas", o.recebe24horas);
2073    setToggleValue("aceita-placa-clube", o.aceitaPlacaClube);
2074    setMultiselectValues("ms-tipos-veiculos", o.tiposVeiculos || []);
2075    setMultiselectValues("ms-marcas-veiculos", o.marcasVeiculos || []);
2076    setMultiselectValues("ms-especializacoes", o.especializacoes || []);
2077  }
2078
2079  const infra = data.estrutura?.infraestrutura;
2080  if (infra) {
2081    setToggleValue("possui-recepcao", infra.possuiRecepcao);
2082    setMultiselectValues("ms-banheiros", infra.banheiros || []);
2083    setToggleValue("possui-local-pecas", infra.localPecas);
2084    setToggleValue("possui-local-sucatas", infra.localSucatas);
2085    setToggleValue("possui-local-residuos", infra.localResiduos);
2086  }
2087
2088  const f = data.ferramental;
2089  if (f) {
2090    setToggleValue("mesa-estiramento", f.funilaria?.mesaEstiramento);
2091    setToggleValue("ciborg", f.funilaria?.ciborg);
2092    setToggleValue("spotter", f.funilaria?.spotter);
2093    setMultiselectValues("ms-tipos-solda", f.funilaria?.tiposSolda || []);
2094    setToggleValue("mixer-tintas", f.pintura?.mixerTintas);
2095    setToggleValue("base-agua", f.pintura?.baseAgua);
2096    setInputValue("marcas-tinta", f.pintura?.marcasTinta);
2097    setMultiselectValues("ms-tipo-lixamento", f.pintura?.tipoLixamento || []);
2098    setToggleValue("plano-aspirante", f.pintura?.planoAspirante);
2099    setToggleValue("cabine-pintura", f.pintura?.cabinePintura);
2100    setToggleValue("cabine-aquecimento", f.pintura?.cabineAquecimento);
2101    setToggleValue("scanner", f.diagnostico?.scanner);
2102    setInputValue("scanner-marca", f.diagnostico?.marcaScanner);
2103    setInputValue("scanner-modelo", f.diagnostico?.modeloScanner);
2104    setToggleValue("ferramentas-plastico", f.reparacaoEspecial?.ferramentasPlastico);
2105    setInputValue("marca-plastico", f.reparacaoEspecial?.marcaPlastico);
2106    setToggleValue("ferramentas-aluminio", f.reparacaoEspecial?.ferramentasAluminio);
2107    setInputValue("marca-aluminio", f.reparacaoEspecial?.marcaAluminio);
2108  }
2109
2110  // Profissionais e suporte - restaura inputs numéricos nas tabelas
2111  const prof = data.estrutura?.profissionais;
2112  if (prof) {
2113    const rows = document.querySelectorAll("#step3-content .professionals-table:first-of-type tbody tr");
2114    const funcoes = ["orcamentista","compradorPecas","funileiro","preparador","pintor","colorista","montador","estoquista","mecanico","polidor","martelinho"];
2115    rows.forEach((row, i) => {
2116      const fn = funcoes[i];
2117      if (fn && prof[fn]) {
2118        const inputs = row.querySelectorAll('input[type="number"]');
2119        if (inputs[0]) inputs[0].value = prof[fn].qtdProprio || 0;
2120        if (inputs[1]) inputs[1].value = prof[fn].qtdTerceirizado || 0;
2121      }
2122    });
2123  }
2124  const sup = data.estrutura?.suporte;
2125  if (sup) {
2126    const rows = document.querySelectorAll("#step3-content .professionals-table:last-of-type tbody tr");
2127    const funcoes = ["recepcao","financeiro","administrativo"];
2128    rows.forEach((row, i) => {
2129      const fn = funcoes[i];
2130      if (fn && sup[fn]) {
2131        const inputs = row.querySelectorAll('input[type="number"]');
2132        if (inputs[0]) inputs[0].value = sup[fn].qtdProprio || 0;
2133        if (inputs[1]) inputs[1].value = sup[fn].qtdTerceirizado || 0;
2134      }
2135    });
2136  }
2137}
2138
2139// ========== LEAD PARCIAL (ABANDONO) ==========
2140function sendPartialLead() {
2141  if (formSubmitted) return;
2142  const cnpj = (getInputValue("cnpj") || "").replace(/\D/g, "");
2143  const telefone = getInputValue("telefone");
2144  const email = getInputValue("email");
2145  if (!cnpj && !telefone && !email) return;
2146
2147  if (LEAD_API_URL && LEAD_API_KEY) {
2148    // Modo API Gateway (AWS)
2149    const formData = collectFormData();
2150    formData.cnpj = formData.dadosCadastrais?.cnpj || "";
2151    formData.status = "Pendente";
2152    formData.etapaAbandono = currentStep;
2153    fetch(LEAD_API_URL, {
2154      method: "POST",
2155      headers: { "Content-Type": "application/json", "x-api-key": LEAD_API_KEY },
2156      body: JSON.stringify(formData),
2157      keepalive: true,
2158    }).catch(() => {});
2159  } else if (LEAD_PARCIAL_URL) {
2160    // Modo Google Apps Script (legado)
2161    const formData = collectFormData();
2162    formData.status = "Pendente";
2163    formData.etapaAbandono = currentStep;
2164    formData.timestamp = new Date().toISOString();
2165    const blob = new Blob([JSON.stringify(formData)], { type: "application/json" });
2166    navigator.sendBeacon(LEAD_PARCIAL_URL, blob);
2167  }
2168}
2169
2170// ========== INICIALIZACAO ==========
2171function init() {
2172  // Inicializa todos os multiselects
2173  Object.keys(MULTISELECT_OPTIONS).forEach((id) => {
2174    initMultiselect(id);
2175  });
2176
2177  // Inicializa todos os custom selects
2178  initAllCustomSelects();
2179
2180  bindAlertModalInteractions(document.getElementById("alert-modal"));
2181
2182  // Fecha dropdown ao clicar fora
2183  document.addEventListener("click", function (e) {
2184    // Fecha multiselects
2185    if (!e.target.closest(".multiselect")) {
2186      document
2187        .querySelectorAll(".multiselect-dropdown.open")
2188        .forEach((dropdown) => {
2189          dropdown.classList.remove("open");
2190          dropdown
2191            .closest(".multiselect")
2192            .querySelector(".multiselect-trigger")
2193            .classList.remove("active");
2194        });
2195    }
2196    // Fecha custom-selects
2197    if (!e.target.closest(".custom-select")) {
2198      document
2199        .querySelectorAll(".custom-select-dropdown.open")
2200        .forEach((dropdown) => {
2201          dropdown.classList.remove("open");
2202          dropdown
2203            .closest(".custom-select")
2204            .querySelector(".custom-select-trigger")
2205            .classList.remove("active");
2206        });
2207    }
2208  });
2209
2210  // Previne que cliques dentro do dropdown fechem ele
2211  document
2212    .querySelectorAll(".multiselect-dropdown, .custom-select-dropdown")
2213    .forEach((dropdown) => {
2214      dropdown.addEventListener("click", function (e) {
2215        e.stopPropagation();
2216      });
2217    });
2218
2219  // Aplica mascaras
2220  const cnpjInput = document.getElementById("cnpj");
2221  const cepInput = document.getElementById("cep");
2222  const telefoneInput = document.getElementById("telefone");
2223
2224  if (cnpjInput) {
2225    cnpjInput.addEventListener("input", (e) => {
2226      formatCNPJ(e.target);
2227      scheduleCnpjLookup(e.target.value);
2228      if (e.target.value.replace(/\D/g, "").length < 14) {
2229        setCnpjLookupStatus("idle");
2230      }
2231      // Tenta restaurar rascunho ao completar CNPJ
2232      const clean = e.target.value.replace(/\D/g, "");
2233      if (clean.length === 14) {
2234        const draft = loadDraft(clean);
2235        if (draft) {
2236          restoreDraft(draft);
2237          setCnpjLookupStatus("success");
2238          const statusEl = document.getElementById("cnpj-lookup-status");
2239          if (statusEl) statusEl.textContent = "— Rascunho restaurado";
2240        }
2241      }
2242    });
2243    cnpjInput.addEventListener("blur", (e) =>
2244      scheduleCnpjLookup(e.target.value, true),
2245    );
2246    scheduleCnpjLookup(cnpjInput.value, true);
2247  }
2248  if (cepInput) {
2249    cepInput.addEventListener("input", (e) => {
2250      formatCEP(e.target);
2251      scheduleCepLookup(e.target.value);
2252    });
2253    cepInput.addEventListener("blur", (e) =>
2254      scheduleCepLookup(e.target.value, true),
2255    );
2256    scheduleCepLookup(cepInput.value, true);
2257  }
2258  if (telefoneInput)
2259    telefoneInput.addEventListener("input", (e) => formatPhone(e.target));
2260
2261  // Atualiza info mobile inicial
2262  updateMobileProgressInfo();
2263
2264  // Auto-save: escuta mudanças em todos os campos do formulário
2265  document.addEventListener("input", scheduleDraftSave);
2266  document.addEventListener("click", (e) => {
2267    if (e.target.closest(".toggle-btn") || e.target.closest(".multiselect-option")) {
2268      scheduleDraftSave();
2269    }
2270  });
2271
2272  // Lead parcial: envia dados ao sair da página
2273  window.addEventListener("beforeunload", sendPartialLead);
2274  document.addEventListener("visibilitychange", () => {
2275    if (document.visibilityState === "hidden") sendPartialLead();
2276  });
2277}
2278
2279document.addEventListener("DOMContentLoaded", init);
2280init()

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.