{"context":{"domain":"natura-br"},"layers":{"ApplicationLayer":{"reference":"ApplicationLayer","availableInstances":[{"reference":"ComponentsDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}},{"component":"data2-core@DataSet","reference":"SystemsDataSet","params":{"filter":{"domain":"data2"},"pagination":{"size":1},"datasource":{"component":"data2-core-types@ProcessReference","params":{"reference":"SystemsDataSource"}}}},{"reference":"ExternalComponentInstancesDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}},{"reference":"SystemsDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[{"_id":"5e3b796d6228227ca637d0f4","name":"data2","domain":"data2","organization":"62292184611f480b3191c37c","visibility":"public","updatedAt":"2026-07-27T14:19:36.810Z"}]}},{"reference":"OrganizationsDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}},{"reference":"DataComponentInstancesDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}},{"component":"data2-core@DataSet","reference":"OneFileComponentInstancesDataSet","params":{"pagination":{"size":1},"master":{"dataset":{"component":"data2-core-types@ProcessReference","params":{"reference":"SystemsDataSet"}},"localField":"system","masterField":"_id"},"datasource":{"component":"data2-core-types@ProcessReference","params":{"reference":"FileComponentInstancesDataSource"}}}},{"reference":"ApplicationsDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}},{"reference":"ComponentInstancesDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}},{"reference":"ServiceComponentInstancesDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}},{"component":"data2-core@Data2TableDataSource","reference":"FileComponentInstancesDataSource","params":{"table":"fileComponentInstances","schema":[{"component":"data2-core-types@Field","params":{"key":"component","title":"Component","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"system","title":"System","component":"data2-core-primitives@ObjectId"}},{"component":"data2-core-types@Field","params":{"key":"displayName","title":"Display Name","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"params","title":"Params","component":"data2-core-primitives@Object"}},{"component":"data2-core-types@Field","params":{"key":"reference","title":"Reference","component":"data2-core-primitives@String"}}]}},{"component":"data2-core@MemoryDataSource","reference":"EmptyDataSource","params":{"initialData":[]}},{"reference":"ComponentsDataSet","component":"data2-core@DataSet","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"EmptyDataSource"}}}},{"reference":"UserComponentsDataSet","component":"data2-core@DataSet","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"EmptyDataSource"}}}},{"reference":"DataComponentInstancesDataSet","component":"data2-core@DataSet","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"EmptyDataSource"}}}},{"reference":"ServiceComponentInstancesDataSet","component":"data2-core@DataSet","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"EmptyDataSource"}}}},{"reference":"ComponentInstancesDataSet","component":"data2-core@DataSet","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"EmptyDataSource"}}}},{"reference":"ExternalComponentInstancesDataSet","component":"data2-core@DataSet","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"EmptyDataSource"}}}},{"reference":"ApplicationsDataSet","component":"data2-core@DataSet","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"EmptyDataSource"}}}}],"processesParams":{}},"HostApplicationLayer":{"reference":"HostApplicationLayer","availableInstances":[{"_id":"5f15ff190528b92601890f71","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"SystemsTableDataSource"}},"filter":{"domain":{"component":"data2-core-types@Reference","params":{"reference":"domain"}}},"pk":"_id","fields":[{"component":"data2-core-types@FieldReference","params":{"key":"_id"}},{"component":"data2-core-types@FieldReference","params":{"key":"name"}},{"component":"data2-core-types@FieldReference","params":{"key":"domain"}},{"component":"data2-core-types@FieldReference","params":{"key":"visibility"}},{"component":"data2-core-types@FieldReference","params":{"key":{"component":"data2-core-primitives@String","params":{"content":"customDomains"}}},"break":true},{"component":"data2-core-types@FieldReference","params":{"key":{"component":"data2-core-primitives@String","params":{"content":"organization._id"}}},"break":true},{"component":"data2-core-types@FieldReference","params":{"key":{"component":"data2-core-primitives@String","params":{"content":"organization.name"}}},"break":true},{"component":"data2-core-types@FieldReference","params":{"key":{"component":"data2-core-primitives@String","params":{"content":"organization.billingStatus"}}},"break":true},{"component":"data2-core-types@FieldReference","params":{"key":{"component":"data2-core-primitives@String","params":{"content":"organization.publicTrial.active"}}},"break":true},{"component":"data2-core-types@FieldReference","params":{"key":{"component":"data2-core-primitives@String","params":{"content":"settings.defaultAccessPolicy"}}},"break":true}],"pagination":{"size":{"component":"data2-core-primitives@Number","params":{"content":1}}},"staticPagination":{"size":{"component":"data2-core-primitives@Number","params":{"content":1}}},"policies":[{"component":"data2-core-auth@Policy","params":{"allow":{"params":{"actions":[{"params":{"components":[{"params":{"query":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$params.query"}}}},"field":{"component":"data2-core-primitives@String","params":{"content":"_id"}},"fieldType":"string"},"component":"data2@7KkQq0vK4a7D2gg6IVJT7"},{"params":{"query":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$params.query"}}}},"field":{"component":"data2-core-primitives@String","params":{"content":"domain"}},"fieldType":"string"},"component":"data2@7KkQq0vK4a7D2gg6IVJT7"},{"params":{"query":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$params.query"}}}},"field":{"component":"data2-core-primitives@String","params":{"content":"customDomains"}},"fieldType":"string"},"component":"data2@7KkQq0vK4a7D2gg6IVJT7"}]},"component":"data2-core-logical-operators@Or"}]},"component":"data2-core-flow@Series"}}}]},"reference":"SystemsDataSet","updatedAt":"2026-08-07T21:51:24.037Z","createdAt":"2020-07-20T20:31:21.838Z"},{"_id":"5f1601ce0528b92601890f7c","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"pagination":{"size":1000},"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"ComponentsTableDataSource"}},"sort":{"params.package":1,"params.component":1},"pk":"_id","autoSelect":{"component":"data2-core-primitives@Boolean","params":{"content":false}},"staticFilter":{"system":{"exists":{"params":{"content":false},"component":"data2-core-primitives@Boolean"}}},"filter":{"system":{"exists":{"params":{"content":false},"component":"data2-core-primitives@Boolean"}}}},"reference":"ComponentsDataSet","updatedAt":"2025-11-05T19:37:01.935Z","createdAt":"2020-07-20T20:42:54.898Z"},{"_id":"5f9041dbc8da12be8b94aea4","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"pagination":{"size":2000},"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"ComponentsTableDataSource"}},"pk":"_id","autoEdit":true,"master":{"dataset":{"component":"data2-core-types@DataSetReference","params":{"reference":"SystemsDataSet"}},"localField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"system"}},"masterField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"_id"}}},"autoSelect":{"component":"data2-core-primitives@Boolean","params":{"content":false}},"policies":{"params":{"allowGet":{"component":"data2-core-primitives@Boolean","params":{"content":true}}},"component":"data2@hkXy-UhMjhyfptdE6_fpW"}},"reference":"UserComponentsDataSet","updatedAt":"2025-11-06T21:06:58.095Z","createdAt":"2020-07-20T20:44:09.350Z"},{"_id":"65d8c4fabc9dbb8f94de8d53","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"ExternalComponentInstancesTableDataSource"}},"pk":"_id","autoEdit":true,"master":{"params":{"dataset":{"component":"data2-core-types@DataSetReference","params":{"reference":"SystemsDataSet"}},"localField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"system"}},"masterField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"_id"}}},"component":"data2-core-types@DataSetMaster"},"autoSelect":{"component":"data2-core-primitives@Boolean","params":{"content":false}},"policies":{"params":{"allowGet":{"component":"data2-core-primitives@Boolean","params":{"content":true}}},"component":"data2@hkXy-UhMjhyfptdE6_fpW"}},"reference":"ExternalComponentInstancesDataSet","updatedAt":"2025-11-06T21:07:23.742Z","createdAt":"2024-02-23T16:17:01.816Z"},{"reference":"SystemsTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[{"_id":"65a86528721f5a092783289d","name":"Natura Brasil CB","domain":"natura-br","visibility":"private","settings":{"defaultAccessPolicy":"private"},"updatedAt":"2026-08-19T20:06:13.815Z","createdAt":"2024-01-17T23:39:20.806Z"}]}},{"reference":"ComponentsTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[{"_id":"5f52a278a575399a45d239b4","component":"data2-core-types@Component","params":{"package":"data2-core-primitives","component":"Router","interactions":{"component":"data2-core-types@FieldSet","params":{"fields":{"goBack":{"component":"data2-core-types@ComponentParameter","params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-types@Function"}},"definition":{"component":"data2-core-functions@Function","params":{"body":{"preventEvaluation":true,"component":"data2-core-types@Getter","params":{"module":{"component":"data2-core-types@This","params":{}},"prop":"history.goBack","args":[]}}}},"icon":{"component":"data2-core-primitives@String","params":{"content":"arrow-left"}},"visibility":"public"}},"goTo":{"component":"data2-core-types@ComponentParameter","params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@Function"}},"definition":{"component":"data2-core-functions@Function","params":{"body":{"preventEvaluation":true,"component":"data2-core-types@Getter","params":{"module":{"component":"data2-core-types@This","params":{}},"prop":"history.push","args":[{"component":"data2-core-types@Reference","params":{"reference":"url"}},{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"preserveQuery"}}}}]}},"parameters":[{"component":"data2-core-primitives@String","params":{"content":"url"}},{"component":"data2-core-primitives@String","params":{"content":"preserveQuery"}}]},"break":true},"icon":{"component":"data2-core-primitives@String","params":{"content":"share"}},"visibility":"public","arguments":[{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@String"}},"title":{"component":"data2-core-primitives@String","params":{"content":"Path to route"}},"description":{"component":"data2-core-primitives@String","params":{"content":"Path to redirect user"}},"visibility":"public","required":{"component":"data2-core-primitives@Boolean","params":{"content":true}}},"component":"data2-core-types@ComponentParameter"},{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@Boolean"}},"title":{"component":"data2-core-primitives@String","params":{"content":"Preserve Query"}},"description":{"component":"data2-core-primitives@String","params":{"content":"Preserve query string parameters  preserve query parameters on navigating."}},"visibility":"public","required":{"component":"data2-core-primitives@Boolean","params":{"content":true}}},"component":"data2-core-types@ComponentParameter"}]}},"open":{"component":"data2-core-types@ComponentParameter","params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-types@Function"}},"definition":{"component":"data2-core-functions@Function","params":{"body":{"preventEvaluation":true,"component":"data2-core-types@Getter","params":{"module":{"component":"data2-core-types@This","params":{}},"prop":"history.push","args":[{"pathname":{"component":"data2-core-types@Reference","params":{"reference":"value"}}}]}},"parameters":["value"]}},"icon":{"component":"data2-core-primitives@String","params":{"content":"share"}},"visibility":"public","arguments":[{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@String"}},"title":{"component":"data2-core-primitives@String","params":{"content":"Link to open"}},"description":{"component":"data2-core-primitives@String","params":{"content":""}},"visibility":"public","required":{"component":"data2-core-primitives@Boolean","params":{"content":true}}},"component":"data2-core-types@ComponentParameter"},{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@String"}},"title":{"component":"data2-core-primitives@String","params":{"content":"Target"}},"description":{"component":"data2-core-primitives@String","params":{"content":"Where to open the link"}},"visibility":"public","required":{"component":"data2-core-primitives@Boolean","params":{"content":false}},"oneOf":[{"params":{"content":"_blank"},"component":"data2-core-primitives@String"},{"params":{"content":"_self"},"component":"data2-core-primitives@String"},{"params":{"content":"_parent"},"component":"data2-core-primitives@String"},{"params":{"content":"_top"},"component":"data2-core-primitives@String"}]},"component":"data2-core-types@ComponentParameter"}]}},"close":{"component":"data2-core-types@ComponentParameter","params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-types@Function"}},"definition":{"component":"data2-core-functions@Function","params":{"body":{"preventEvaluation":true,"component":"data2-core-types@Getter","params":{"module":{"params":{},"component":"data2-core-web@Window"},"prop":"close"}},"parameters":["value"]}},"icon":{"component":"data2-core-primitives@String","params":{"content":"times"}},"visibility":"public","arguments":[{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@String"}},"title":{"component":"data2-core-primitives@String","params":{"content":"Link to open"}},"description":{"component":"data2-core-primitives@String","params":{"content":""}},"visibility":"public","required":{"component":"data2-core-primitives@Boolean","params":{"content":true}}},"component":"data2-core-types@ComponentParameter"},{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@String"}},"title":{"component":"data2-core-primitives@String","params":{"content":"Target"}},"description":{"component":"data2-core-primitives@String","params":{"content":"Where to open the link"}},"visibility":"public","required":{"component":"data2-core-primitives@Boolean","params":{"content":false}},"oneOf":[{"params":{"content":"_blank"},"component":"data2-core-primitives@String"},{"params":{"content":"_self"},"component":"data2-core-primitives@String"},{"params":{"content":"_parent"},"component":"data2-core-primitives@String"},{"params":{"content":"_top"},"component":"data2-core-primitives@String"}]},"component":"data2-core-types@ComponentParameter"}],"description":{"component":"data2-core-primitives@String","params":{"content":"Closes a window that was opened via router.open."}}}}}}}}},{"_id":"620d28658271aa4129919d89","component":"data2-core-types@Component","params":{"package":"data2-interface-services","component":"DeviceController","interactions":{"params":{"fields":{"postMessage":{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@Function"}},"arguments":[{"component":"data2-core-types@ComponentParameter","params":{"component":"data2-core-primitives@String","title":{"component":"data2-core-primitives@String","params":{"content":"data"}}}}]},"component":"data2-core-types@ComponentParameter"},"print":{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@Function"}},"arguments":[{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@Object"}},"flat":{"component":"data2-core-primitives@Boolean","params":{"content":true}},"parameters":{"zoom":{"component":"data2-core-types@ComponentParameter","params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@Number"}},"title":{"component":"data2-core-primitives@String","params":{"content":"zoom"}},"visibility":"public"}}},"visibility":"public"},"component":"data2-core-types@ComponentParameter"}],"visibility":"public"},"component":"data2-core-types@ComponentParameter"},"share":{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@Function"}}},"component":"data2-core-types@ComponentParameter"},"clipboard.copy":{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@Function"}},"arguments":[{"component":"data2-core-types@ComponentParameter","params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@String"}}}}]},"component":"data2-core-types@ComponentParameter"},"clipboard.paste":{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@Function"}},"arguments":[{"component":"data2-core-types@ComponentParameter","params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@String"}}}}]},"component":"data2-core-types@ComponentParameter"},"download":{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@Function"}},"visibility":"public","arguments":[{"component":"data2-core-types@ComponentParameter","params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@String"}},"title":{"component":"data2-core-primitives@String","params":{"content":"Filename"}}}},{"component":"data2-core-types@ComponentParameter","params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@String"}},"title":{"component":"data2-core-primitives@String","params":{"content":"Content"}}}},{"component":"data2-core-types@ComponentParameter","params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@Object"}},"title":{"component":"data2-core-primitives@String","params":{"content":"Options"}},"parameters":{"params":{"fields":{"type":{"component":"data2-core-types@ComponentParameter","params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@String"}},"title":{"component":"data2-core-primitives@String","params":{"content":"Mimetype"}}}}}},"component":"data2-core-types@FieldSet"}}}]},"component":"data2-core-types@ComponentParameter"},"downloadUrl":{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@Function"}},"visibility":"public","arguments":[{"component":"data2-core-types@ComponentParameter","params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@String"}},"title":{"component":"data2-core-primitives@String","params":{"content":"Filename"}}}},{"component":"data2-core-types@ComponentParameter","params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@String"}},"title":{"component":"data2-core-primitives@String","params":{"content":"Url"}}}}]},"component":"data2-core-types@ComponentParameter"},"promptFiles":{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@Function"}},"arguments":[{"component":"data2-core-types@ComponentParameter","params":{"component":"data2-core-primitives@Object","flat":{"component":"data2-core-primitives@Boolean","params":{"content":true}},"parameters":{"params":{"fields":{"multiple":{"component":"data2-core-types@ComponentParameter","params":{"title":{"component":"data2-core-primitives@String","params":{"content":"Multiple"}},"description":{"component":"data2-core-primitives@String","params":{"content":"Whether or not to allow selecting multiple files"}},"component":"data2-core-primitives@Boolean"}},"acceptedFormats":{"component":"data2-core-types@ComponentParameter","params":{"title":{"component":"data2-core-primitives@String","params":{"content":"Accepted Formats"}},"description":{"component":"data2-core-primitives@String","params":{"content":"Accepted file formats"}},"component":"data2-core-primitives@String","list":{"component":"data2-core-primitives@Boolean","params":{"content":true}}}},"timeout":{"component":"data2-core-types@ComponentParameter","params":{"title":{"component":"data2-core-primitives@String","params":{"content":"Timeout"}},"description":{"component":"data2-core-primitives@String","params":{"content":"Max milliseconds to wait for file upload"}},"component":"data2-core-primitives@Number"}}}},"component":"data2-core-types@FieldSet"}}}],"visibility":"public"},"component":"data2-core-types@ComponentParameter"},"meetingRecord":{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@Function"}},"visibility":"public","definition":{"params":{"actions":[{"params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"meeting"}},"script":{"component":"data2-core-primitives@String","params":{"content":"(function() {\n  let mediaRecorder;\n  let audioChunks = [];\n  let combinedStream;\n  let audioContext;\n  \n  window.meetingCapture = async function() {\n    try {\n      // Solicitar acesso ao áudio da guia (e vídeo, que será ignorado depois)\n      const tabStream = await navigator.mediaDevices.getDisplayMedia({ \n        video: true, // Necessário para alguns navegadores\n        audio: true\n      });\n      \n      // Remover a trilha de vídeo\n      const tabAudioTrack = tabStream.getAudioTracks()[0];\n      \n      // Solicitar acesso ao microfone\n      const micStream = await navigator.mediaDevices.getUserMedia({ audio: true });\n      const micAudioTrack = micStream.getAudioTracks()[0];\n      \n      // Criar um novo MediaStream com ambas as trilhas de áudio\n      combinedStream = new MediaStream([tabAudioTrack, micAudioTrack]);\n      \n      // Criar um contexto de áudio para misturar os streams\n      audioContext = new AudioContext();\n      const destination = audioContext.createMediaStreamDestination();\n      \n      // Conectar a trilha de áudio da guia\n      const tabSource = audioContext.createMediaStreamSource(new MediaStream([tabAudioTrack]));\n      tabSource.connect(destination);\n      \n      // Conectar a trilha de áudio do microfone\n      const micSource = audioContext.createMediaStreamSource(new MediaStream([micAudioTrack]));\n      micSource.connect(destination);\n      \n      // Iniciar gravação com o stream misturado\n      mediaRecorder = new MediaRecorder(destination.stream);\n      audioChunks = [];\n      mediaRecorder.ondataavailable = (e) => audioChunks.push(e.data);\n      mediaRecorder.start();\n      \n      console.log('Gravação iniciada. Chame meetingCaptureStop() para finalizar.');\n    } catch (error) {\n      console.error('Erro ao iniciar a captura:', error);\n    }\n  };\n  \n  window.meetingCaptureStop = function() {\n    return new Promise((resolve, reject) => {\n      if (!mediaRecorder) {\n        reject('Nenhuma gravação em andamento.');\n        return;\n      }\n      \n      mediaRecorder.onstop = () => {\n        const audioBlob = new Blob(audioChunks, { type: 'audio/webm' });\n        combinedStream.getTracks().forEach(track => track.stop());\n        audioContext.close();\n        \n        resolve(audioBlob);\n        console.log('Gravação finalizada. O arquivo de áudio está pronto para upload.');\n      };\n      \n      mediaRecorder.stop();\n    });\n  };\n})();"}}},"component":"data2-core-web@Script"},{"component":"data2-core-types@Getter","params":{"module":{"params":{},"component":"data2-core-web@Window"},"prop":{"component":"data2-core-primitives@String","params":{"content":"meetingCapture"}}}}]},"component":"data2-core-flow@Series"}},"component":"data2-core-types@ComponentParameter"},"meetingRecordStop":{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@Function"}},"visibility":"public","arguments":[{"component":"data2-core-types@ComponentParameter","params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@String"}},"title":{"component":"data2-core-primitives@String","params":{"content":"Filename"}}}},{"component":"data2-core-types@ComponentParameter","params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@String"}},"title":{"component":"data2-core-primitives@String","params":{"content":"Content"}}}},{"component":"data2-core-types@ComponentParameter","params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@Object"}},"title":{"component":"data2-core-primitives@String","params":{"content":"Options"}},"parameters":{"params":{"fields":{"type":{"component":"data2-core-types@ComponentParameter","params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@String"}},"title":{"component":"data2-core-primitives@String","params":{"content":"Mimetype"}}}}}},"component":"data2-core-types@FieldSet"}}}],"definition":{"params":{"actions":[{"component":"data2-core-types@Getter","params":{"module":{"params":{},"component":"data2-core-web@Window"},"prop":{"component":"data2-core-primitives@String","params":{"content":"meetingCaptureStop"}}}}]},"component":"data2-core-flow@Series"}},"component":"data2-core-types@ComponentParameter"},"notify":{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@Function"}},"visibility":"private","arguments":[{"component":"data2-core-types@ComponentParameter","params":{"component":"data2-core-primitives@Object","parameters":{"title":{"params":{"component":"data2-core-primitives@String","index":{"component":"data2-core-primitives@Number","params":{"content":0}},"title":{"component":"data2-core-primitives@String","params":{"content":"title"}}},"component":"data2-core-types@ComponentParameter"},"sound":{"params":{"component":"data2-core-types@FileComponentUrlReference","index":{"component":"data2-core-primitives@Number","params":{"content":1}},"title":{"component":"data2-core-primitives@String","params":{"content":"sound"}}},"component":"data2-core-types@ComponentParameter"}},"flat":{"component":"data2-core-primitives@Boolean","params":{"content":true}}}}]},"component":"data2-core-types@ComponentParameter"}}},"component":"data2-core-types@FieldSet"}}},{"_id":"6a74bb8c54c8818182efe799","component":"data2-core-types@Component","system":"65a86528721f5a092783289d","params":{"extends":"data2-core-primitives@Block","component":"kUraDashboard","package":"natura-br","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"export default {\n  css: `\n    :host { display:block; width:100%; height:100%; }\n    .wrap { font-family: ui-sans-serif, -apple-system, \"Segoe UI\", Roboto, sans-serif;\n            color:#1b1b1f; background:#fafafa; padding:26px 30px 34px; width:100%;\n            min-height:100%; box-sizing:border-box; }\n    .head { display:flex; align-items:baseline; justify-content:space-between; gap:16px;\n            flex-wrap:wrap; margin-bottom:16px; }\n    h1 { font-size:20px; font-weight:650; margin:0; letter-spacing:-0.01em; }\n    .sub { font-size:12.5px; color:#6b6b73; }\n    .mono { font-family: ui-monospace, \"SF Mono\", Menlo, monospace; font-size:11.5px; }\n\n    .filtros { display:flex; gap:10px; flex-wrap:wrap; align-items:flex-end;\n               background:#fff; border:1px solid #e8e8ec; border-radius:10px;\n               padding:13px 15px; margin-bottom:20px; }\n    .fgrp { display:flex; flex-direction:column; gap:5px; }\n    .fgrp label { font-size:10px; text-transform:uppercase; letter-spacing:.06em;\n                  color:#8a8a93; font-weight:650; }\n    select, input { font:inherit; font-size:12.5px; padding:6px 9px; border-radius:7px;\n                    border:1px solid #dcdce2; background:#fff; color:#1b1b1f; min-width:132px;\n                    box-sizing:border-box; }\n    input { min-width:118px; }\n    select:focus, input:focus { outline:2px solid #c9d6ee; outline-offset:-1px; border-color:#a9c3ea; }\n    .limpar { margin-left:auto; align-self:flex-end; font-size:12px; color:#6b6b73;\n              background:none; border:1px solid #dcdce2; border-radius:7px; padding:7px 12px;\n              cursor:pointer; font-family:inherit; }\n    .limpar:hover { background:#f4f4f7; }\n    .contagem { align-self:flex-end; font-size:12px; color:#6b6b73; padding-bottom:7px; }\n\n    .grid { display:grid; gap:13px; grid-template-columns:repeat(5,minmax(0,1fr));\n            margin-bottom:22px; }\n    @media (max-width:1180px) { .grid { grid-template-columns:repeat(3,minmax(0,1fr)); } }\n    @media (max-width:720px) { .grid { grid-template-columns:repeat(2,minmax(0,1fr)); } }\n    .kpi { background:#fff; border:1px solid #e8e8ec; border-radius:10px; padding:14px 15px; }\n    .kpi .lbl { font-size:10.5px; text-transform:uppercase; letter-spacing:.06em;\n                color:#8a8a93; font-weight:650; line-height:1.35; }\n    .kpi .val { font-size:24px; font-weight:640; margin-top:6px; letter-spacing:-0.02em;\n                font-variant-numeric:tabular-nums; }\n    .kpi .hint { font-size:11px; color:#8a8a93; margin-top:3px; }\n    .kpi.warn .val { color:#b4402a; }\n\n    .cols { display:grid; gap:16px; grid-template-columns:repeat(auto-fit,minmax(300px,1fr));\n            margin-bottom:16px; }\n    .card { background:#fff; border:1px solid #e8e8ec; border-radius:10px; padding:16px 17px 19px; }\n    .card.full { grid-column:1/-1; }\n    .card h2 { font-size:12px; text-transform:uppercase; letter-spacing:.06em;\n               color:#6b6b73; margin:0 0 13px; font-weight:650; }\n\n    .bar { display:grid; grid-template-columns:1fr auto; gap:10px; align-items:center;\n           margin-bottom:8px; }\n    .bar .track { position:relative; height:22px; background:#f2f2f5; border-radius:5px;\n                  overflow:hidden; }\n    .bar .fill { position:absolute; inset:0 auto 0 0; border-radius:5px; }\n    .bar .name { position:absolute; left:9px; right:9px; top:0; line-height:22px; font-size:12px;\n                 white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }\n    .bar .n { font-variant-numeric:tabular-nums; color:#6b6b73; font-size:12.5px;\n              min-width:78px; text-align:right; }\n\n    table { width:100%; border-collapse:collapse; font-size:12.5px; }\n    th { text-align:left; font-size:10.5px; text-transform:uppercase; letter-spacing:.05em;\n         color:#8a8a93; font-weight:650; padding:0 8px 8px 0; border-bottom:1px solid #ececf0; }\n    td { padding:8px 8px 8px 0; border-bottom:1px solid #f4f4f7;\n         font-variant-numeric:tabular-nums; vertical-align:top; }\n    tr.sess { cursor:pointer; }\n    tr.sess:hover td { background:#fbfbfd; }\n    tr.sess.on td { background:#f5f7fb; }\n    .caret { display:inline-block; width:12px; color:#a0a0a8; }\n\n    .pill { display:inline-block; padding:2px 8px; border-radius:99px; font-size:11px;\n            font-weight:650; white-space:nowrap; }\n    .p-retida { background:#e6f4ea; color:#1c6b3a; }\n    .p-devolvida { background:#fdf0e3; color:#8a5220; }\n    .p-abandono_pre_agente, .p-abandono_durante { background:#fbeaea; color:#98342c; }\n    .p-queda_tecnica { background:#eee9fb; color:#54399b; }\n    .p-sem_classificacao { background:#f0f0f3; color:#6b6b73; }\n    .p-aberto { background:#fdf0e3; color:#8a5220; }\n\n    .detalhe { background:#f7f8fa; border-left:2px solid #cfd6e2; }\n    .detalhe td { border-bottom:1px solid #eceef2; padding:0; }\n    .svc { display:grid; grid-template-columns:26px 1fr auto auto; gap:10px; align-items:center;\n           padding:7px 12px 7px 14px; font-size:12px; border-bottom:1px solid #eceef2; }\n    .svc:last-child { border-bottom:none; }\n    .svc .ord { color:#a0a0a8; font-size:11px; font-variant-numeric:tabular-nums; }\n    .svc .par { color:#8a8a93; font-size:11px; }\n    .svc .lat { font-variant-numeric:tabular-nums; min-width:56px; text-align:right; }\n    .svc .lat.slow { color:#b4402a; font-weight:650; }\n    .svc .bad { color:#98342c; font-weight:650; }\n    .semsvc { padding:9px 14px; color:#8a8a93; font-size:12px; }\n\n    .empty { color:#8a8a93; font-size:13px; padding:9px 0; }\n    .err { background:#fbeaea; border:1px solid #f0cfcb; color:#98342c; padding:12px 14px;\n           border-radius:8px; font-size:13px; }\n    .foot { margin-top:18px; font-size:11.5px; color:#9a9aa2; line-height:1.65; }\n  `,\n\n  async mounted(d2) {\n    if (!d2.state.f) {\n      d2.state.f = { periodo: 'tudo', outcome: 'todos', consultora: '', servico: 'todos' }\n    }\n    const ref = d2.param('fonteRush', 'poc-ura-metricas')\n    try {\n      const res = await d2.rush(ref).invoke({})\n      const out = (res && res.data) ? res.data : res\n      d2.setState({\n        rows: (out && out.chamadas) || [],\n        svcs: (out && out.servicos) || [],\n        canc: (out && out.cancelamentos) || [],\n        fechadas: {},\n        loaded: true\n      })\n    } catch (e) {\n      d2.setState({ erro: String((e && e.message) || e), loaded: true })\n    }\n\n    const raiz = d2.root\n    const aplicaFiltro = (e) => {\n      const el = e.target && e.target.closest && e.target.closest('[data-f]')\n      if (!el) return\n      const f = Object.assign({}, d2.state.f)\n      f[el.dataset.f] = el.value\n      d2.setState({ f: f })\n    }\n    d2.on(raiz, 'change', aplicaFiltro)\n    d2.on(raiz, 'input', aplicaFiltro)\n    d2.on(raiz, 'click', (e) => {\n      const alvo = e.target\n      if (!alvo || !alvo.closest) return\n      if (alvo.closest('[data-limpar]')) {\n        d2.setState({ f: { periodo: 'tudo', outcome: 'todos', consultora: '', servico: 'todos' } })\n        return\n      }\n      const linha = alvo.closest('[data-sess]')\n      if (!linha) return\n      const id = linha.dataset.sess\n      const fech = Object.assign({}, d2.state.fechadas || {})\n      if (fech[id]) { delete fech[id] } else { fech[id] = 1 }\n      d2.setState({ fechadas: fech })\n    })\n  },\n\n  render(d2) {\n    const titulo = d2.param('titulo', 'URA de voz — POC')\n    if (!d2.state.loaded) return `<div class=\"wrap\"><div class=\"sub\">Carregando…</div></div>`\n    if (d2.state.erro) {\n      return `<div class=\"wrap\"><h1>${titulo}</h1>\n        <div class=\"err\">Não consegui ler as métricas.<br>\n        <span class=\"mono\">${d2.state.erro}</span></div></div>`\n    }\n\n    const f = d2.state.f || { periodo: 'tudo', outcome: 'todos', consultora: '', servico: 'todos' }\n    const todas = d2.state.rows || []\n    const todosSvc = d2.state.svcs || []\n    const canc = d2.state.canc || []\n    const num = (v) => d2.fmt.number(v)\n    const dt = (r) => String(r.timestamp || r.createdAt || '')\n\n    let corte = null\n    if (f.periodo !== 'tudo') {\n      const dias = Number(f.periodo)\n      corte = new Date(Date.now() - dias * 864e5).toISOString()\n    }\n\n    const svcDeCall = {}\n    todosSvc.forEach(s => {\n      const k = s.call_id || '?'\n      svcDeCall[k] = svcDeCall[k] || []\n      svcDeCall[k].push(s)\n    })\n    Object.keys(svcDeCall).forEach(k =>\n      svcDeCall[k].sort((a, b) => (Number(a.ordem) || 0) - (Number(b.ordem) || 0)))\n\n    const rows = todas.filter(r => {\n      if (corte && dt(r) < corte) return false\n      if (f.outcome !== 'todos' && (r.outcome || 'sem_classificacao') !== f.outcome) return false\n      if (f.consultora && !String(r.consultant_id || '').includes(f.consultora.trim())) return false\n      if (f.servico !== 'todos') {\n        const lista = (r.usecases || []).concat((svcDeCall[r.call_id] || []).map(s => s.usecase))\n        if (!lista.some(u => u === f.servico)) return false\n      }\n      return true\n    })\n    const idsVisiveis = {}\n    rows.forEach(r => { idsVisiveis[r.call_id] = 1 })\n    const svcs = todosSvc.filter(s => idsVisiveis[s.call_id])\n\n    const n = rows.length\n    const nAll = todas.length\n    const pct = (a, b) => b ? Math.round((a / b) * 100) + '%' : '—'\n    const sum = (arr, f2) => arr.reduce((t, r) => t + (Number(r[f2]) || 0), 0)\n    const avg = (arr, f2) => { const c = arr.filter(r => r[f2] != null).length; return c ? sum(arr, f2) / c : 0 }\n\n    const byOutcome = {}\n    rows.forEach(r => { const k = r.outcome || 'sem_classificacao'; byOutcome[k] = (byOutcome[k] || 0) + 1 })\n    const retidas = byOutcome.retida || 0\n    const devolvidas = byOutcome.devolvida || 0\n    const abandonos = (byOutcome.abandono_pre_agente || 0) + (byOutcome.abandono_durante || 0)\n    const intencao = rows.filter(r => r.transferred_intent).length\n    const feitas = rows.filter(r => r.transferred_done).length\n    const errosSvc = svcs.filter(s => s.erro === true).length\n    const latVals = svcs.filter(s => s.latencia_s != null).map(s => Number(s.latencia_s))\n    const latMax = latVals.length ? Math.max.apply(null, latVals) : 0\n    const latAvg = latVals.length ? latVals.reduce((a, b) => a + b, 0) / latVals.length : 0\n    const comCsat = rows.filter(r => r.csat_score != null)\n    const csat = comCsat.length ? sum(comCsat, 'csat_score') / comCsat.length : null\n\n    const svcAgg = {}\n    svcs.forEach(s => {\n      const k = s.usecase || '—'\n      svcAgg[k] = svcAgg[k] || { n: 0, lat: [], err: 0 }\n      svcAgg[k].n++\n      if (s.latencia_s != null) svcAgg[k].lat.push(Number(s.latencia_s))\n      if (s.erro === true) svcAgg[k].err++\n    })\n    const svcTop = Object.keys(svcAgg).map(k => {\n      const a = svcAgg[k]\n      const m = a.lat.length ? a.lat.reduce((x, y) => x + y, 0) / a.lat.length : null\n      return { nome: k, n: a.n, lat: m, err: a.err }\n    }).sort((a, b) => b.n - a.n)\n    const svcMax = svcTop.length ? svcTop[0].n : 1\n\n    const motAgg = {}\n    canc.forEach(c => {\n      const k = c.motivo || 'sem motivo registrado'\n      motAgg[k] = (motAgg[k] || 0) + 1\n    })\n    const motTop = Object.keys(motAgg).map(k => [k, motAgg[k]]).sort((a, b) => b[1] - a[1])\n    const motMax = motTop.length ? motTop[0][1] : 1\n\n    const porDia = {}\n    rows.forEach(r => {\n      const dia = dt(r).slice(0, 10)\n      if (!dia) return\n      porDia[dia] = (porDia[dia] || 0) + 1\n    })\n    const dias = Object.keys(porDia).sort().slice(-21)\n    const diaMax = dias.reduce((m, d) => Math.max(m, porDia[d]), 1)\n\n    const cores = {\n      retida: '#8fd0a6', devolvida: '#f2c48d', abandono_pre_agente: '#eba9a2',\n      abandono_durante: '#eba9a2', queda_tecnica: '#bfaeea', sem_classificacao: '#dcdce2'\n    }\n    const ordem = ['retida', 'devolvida', 'abandono_pre_agente', 'abandono_durante', 'queda_tecnica', 'sem_classificacao']\n    const outList = ordem.filter(k => byOutcome[k]).map(k => [k, byOutcome[k]])\n\n    const nomesSvc = Object.keys(\n      todosSvc.reduce((a, s) => { if (s.usecase) a[s.usecase] = 1; return a }, \n        todas.reduce((a, r) => { (r.usecases || []).forEach(u => { if (u) a[u] = 1 }); return a }, {}))\n    ).sort()\n\n    const opt = (v, lbl, cur) => `<option value=\"${v}\"${cur === v ? ' selected' : ''}>${lbl}</option>`\n    const kpi = (lbl, val, hint, warn) =>\n      `<div class=\"kpi${warn ? ' warn' : ''}\"><div class=\"lbl\">${lbl}</div>\n       <div class=\"val\">${val}</div>${hint ? `<div class=\"hint\">${hint}</div>` : ''}</div>`\n    const barra = (nome, valor, max, cor, sufixo) => `\n      <div class=\"bar\"><div class=\"track\">\n        <div class=\"fill\" style=\"width:${max ? (valor / max) * 100 : 0}%;background:${cor}\"></div>\n        <div class=\"name\">${nome}</div></div>\n        <div class=\"n\">${sufixo || num(valor)}</div></div>`\n\n    const linhaSessao = (r) => {\n      const aberta = !(d2.state.fechadas || {})[r.call_id]\n      const lista = svcDeCall[r.call_id] || []\n      const principal = `\n        <tr class=\"sess${aberta ? ' on' : ''}\" data-sess=\"${r.call_id}\" data-key=\"${r._id}\">\n          <td><span class=\"caret\">${aberta ? '▾' : '▸'}</span>\n              <span class=\"mono\">${dt(r).slice(5, 16).replace('T', ' ')}</span></td>\n          <td class=\"mono\">${r.consultant_id || '—'}</td>\n          <td class=\"mono\">${r.caller_number || '—'}</td>\n          <td><span class=\"pill p-${r.outcome || 'sem_classificacao'}\">${(r.outcome || '—').replace(/_/g, ' ')}</span></td>\n          <td style=\"text-align:right\">${r.duration_s != null ? r.duration_s + 's' : '—'}</td>\n          <td style=\"text-align:right\">${lista.length || r.usecases_count || 0}</td>\n          <td style=\"text-align:right\">${r.ai_tokens != null ? d2.fmt.compact(r.ai_tokens) : '—'}</td>\n        </tr>`\n      if (!aberta) return principal\n      const corpo = lista.length\n        ? lista.map(s => `\n            <div class=\"svc\">\n              <div class=\"ord\">${Number(s.ordem) + 1}</div>\n              <div><span class=\"mono\">${s.usecase || '—'}</span>\n                ${s.parametros && s.parametros !== '{}' ? `<span class=\"par\"> ${s.parametros}</span>` : ''}</div>\n              <div class=\"lat${s.latencia_s >= 8 ? ' slow' : ''}\">${s.latencia_s != null ? Number(s.latencia_s).toFixed(2) + 's' : '—'}</div>\n              <div class=\"${s.erro === true ? 'bad' : ''}\">${s.erro === true ? 'erro' : 'ok'}</div>\n            </div>`).join('')\n        : `<div class=\"semsvc\">Serviços por chamada só são gravados a partir de 06/08.\n             Esta sessão registrou: ${(r.usecases || []).join(', ') || '—'}</div>`\n      return principal + `<tr class=\"detalhe\"><td colspan=\"7\">${corpo}</td></tr>`\n    }\n\n    return `\n    <div class=\"wrap\">\n      <div class=\"head\">\n        <div>\n          <h1>${titulo}</h1>\n          <div class=\"sub\">${num(n)} de ${num(nAll)} ${nAll === 1 ? 'chamada' : 'chamadas'}\n            ${dias.length ? ' · ' + dias[0] + ' a ' + dias[dias.length - 1] : ''}\n            · ${num(svcs.length)} serviços consumidos</div>\n        </div>\n        <div class=\"sub mono\">poc-ura-chamadas · poc-ura-servicos</div>\n      </div>\n\n      <div class=\"filtros\">\n        <div class=\"fgrp\"><label>Período</label>\n          <select data-f=\"periodo\">\n            ${opt('tudo', 'Tudo', f.periodo)}${opt('1', 'Hoje', f.periodo)}\n            ${opt('7', 'Últimos 7 dias', f.periodo)}${opt('14', 'Últimos 14 dias', f.periodo)}\n            ${opt('30', 'Últimos 30 dias', f.periodo)}\n          </select></div>\n        <div class=\"fgrp\"><label>Desfecho</label>\n          <select data-f=\"outcome\">\n            ${opt('todos', 'Todos', f.outcome)}\n            ${ordem.map(o => opt(o, o.replace(/_/g, ' '), f.outcome)).join('')}\n          </select></div>\n        <div class=\"fgrp\"><label>Serviço</label>\n          <select data-f=\"servico\">\n            ${opt('todos', 'Todos', f.servico)}\n            ${nomesSvc.map(s => opt(s, s, f.servico)).join('')}\n          </select></div>\n        <div class=\"fgrp\"><label>Consultora</label>\n          <input data-f=\"consultora\" placeholder=\"código\" value=\"${f.consultora || ''}\"></div>\n        <div class=\"contagem\">${n === nAll ? 'sem filtro' : num(nAll - n) + ' fora do filtro'}</div>\n        <button class=\"limpar\" data-limpar>Limpar</button>\n      </div>\n\n      <div class=\"grid\">\n        ${kpi('Retido na IA', pct(retidas, n), num(retidas) + ' de ' + num(n))}\n        ${kpi('Devolvido', pct(devolvidas, n), num(devolvidas) + ' pediram atendente')}\n        ${kpi('Abandono', pct(abandonos, n), num(abandonos) + ' chamadas')}\n        ${kpi('Duração média', Math.round(avg(rows, 'duration_s')) + 's', 'por chamada')}\n        ${kpi('Serviços por sessão', n ? (svcs.length / n).toFixed(1) : '—', num(svcs.length) + ' no total')}\n        ${kpi('Latência média', latVals.length ? latAvg.toFixed(1) + 's' : '—',\n            num(latVals.length) + ' medições')}\n        ${kpi('Latência máxima', latMax ? latMax.toFixed(1) + 's' : '—',\n            latMax >= 8 ? 'acima do tolerável por voz' : 'pico observado', latMax >= 8)}\n        ${kpi('Erros de serviço', num(errosSvc), errosSvc ? 'verificar' : 'nenhum', errosSvc > 0)}\n        ${kpi('Tokens de IA', d2.fmt.compact(sum(rows, 'ai_tokens')),\n            'média ' + d2.fmt.compact(Math.round(avg(rows, 'ai_tokens'))))}\n        ${kpi('CSAT', csat != null ? csat.toFixed(1) : '—',\n            csat != null ? num(comCsat.length) + ' respostas' : 'nenhuma resposta ainda', csat == null)}\n      </div>\n\n      <div class=\"cols\">\n        <div class=\"card\">\n          <h2>Desfecho da chamada</h2>\n          ${outList.length\n            ? outList.map(([k, v]) => barra(k.replace(/_/g, ' '), v, n, cores[k])).join('')\n            : '<div class=\"empty\">sem chamadas no filtro</div>'}\n        </div>\n\n        <div class=\"card\">\n          <h2>Volume por dia</h2>\n          ${dias.length\n            ? dias.map(d => barra(d.slice(5), porDia[d], diaMax, '#b9c0cc')).join('')\n            : '<div class=\"empty\">sem chamadas no filtro</div>'}\n        </div>\n\n        <div class=\"card\">\n          <h2>Transferência</h2>\n          <table>\n            <tr><th>Etapa</th><th style=\"text-align:right\">Chamadas</th></tr>\n            <tr><td>Pediu atendente</td><td style=\"text-align:right\">${num(intencao)}</td></tr>\n            <tr><td>Transferência concluída</td><td style=\"text-align:right\">${num(feitas)}</td></tr>\n          </table>\n          ${intencao && !feitas\n            ? `<div class=\"foot\">Intenção sem conclusão: o sinal de TRANSFER passou a ser enviado\n               em 06/08 — antes disso o destino apontava para <span class=\"mono\">/hangup</span>.</div>`\n            : ''}\n        </div>\n\n        <div class=\"card\">\n          <h2>Motivos de cancelamento</h2>\n          ${motTop.length\n            ? motTop.map(([k, v]) => barra(k, v, motMax, k === 'sem motivo registrado' ? '#dcdce2' : '#f2c48d')).join('')\n            : '<div class=\"empty\">nenhum cancelamento registrado</div>'}\n        </div>\n\n        <div class=\"card full\">\n          <h2>Fila de cancelamentos</h2>\n          ${canc.length\n            ? `<table><tr><th>Protocolo</th><th>Quando</th><th>Consultora</th><th>Pedido</th>\n                 <th>Motivo</th><th>Status</th></tr>\n               ${canc.slice().sort((a, b) => String(b.createdAt || '').localeCompare(String(a.createdAt || '')))\n                 .slice(0, 20).map(c => `<tr data-key=\"${c._id}\">\n                 <td class=\"mono\">${c.protocol_number || '—'}</td>\n                 <td class=\"mono\">${String(c.createdAt || '').slice(5, 16).replace('T', ' ')}</td>\n                 <td class=\"mono\">${c.consultant_id || '—'}</td>\n                 <td class=\"mono\">${c.order_id || '—'}</td>\n                 <td>${c.motivo_codigo ? c.motivo_codigo + ' — ' : ''}${c.motivo || '<span style=\"color:#98342c\">não coletado</span>'}</td>\n                 <td><span class=\"pill p-aberto\">${c.status || '—'}</span></td></tr>`).join('')}\n               </table>\n               <div class=\"foot\">Nada consome esta fila ainda — o registro é honesto, mas alguém\n                 (ou um Scheduler) precisa processar para o prazo de 2 dias úteis valer.</div>`\n            : '<div class=\"empty\">nenhum cancelamento registrado</div>'}\n        </div>\n\n        <div class=\"card full\">\n          <h2>Serviços consumidos — volume, latência média e erros</h2>\n          ${svcTop.length\n            ? svcTop.map(s => barra(\n                s.nome, s.n, svcMax,\n                s.lat != null && s.lat >= 8 ? '#eba9a2' : '#a9c3ea',\n                num(s.n) + (s.lat != null ? ' · ' + s.lat.toFixed(1) + 's' : '') + (s.err ? ' · ' + s.err + ' erro' : '')\n              )).join('')\n            : '<div class=\"empty\">sem serviços no filtro</div>'}\n        </div>\n\n        <div class=\"card full\">\n          <h2>Sessões e serviços consumidos — clique na linha para recolher</h2>\n          ${n\n            ? `<table>\n                <tr><th>Quando</th><th>Consultora</th><th>Telefone</th><th>Desfecho</th>\n                    <th style=\"text-align:right\">Duração</th><th style=\"text-align:right\">Serviços</th>\n                    <th style=\"text-align:right\">Tokens</th></tr>\n                ${rows.slice().sort((a, b) => dt(b).localeCompare(dt(a))).map(linhaSessao).join('')}\n               </table>`\n            : '<div class=\"empty\">nenhuma chamada no filtro</div>'}\n        </div>\n      </div>\n\n      <div class=\"foot\">\n        Uma linha por chamada e uma por serviço consumido, escritas pelo\n        <span class=\"mono\">finish-call-webhook</span> no fim do atendimento.\n        Não coberto aqui: tempo até atender e queda técnica, que vivem no log da borda SIP e\n        precisam de reconciliação — hoje uma queda nossa aparece como abandono.\n        Volume de contato, abandono e transbordo da URA são da Wittel; ocorrências abertas, da Natura.\n      </div>\n    </div>`\n  }\n}"}},"titulo":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"titulo"}}}},"fonteRush":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"fonteRush"}}}}}}}}}},{"_id":"6a75bb3a464ed6666136874b","component":"data2-core-types@Component","system":"65a86528721f5a092783289d","params":{"extends":"data2-core-primitives@Block","component":"kUraConsole","package":"natura-br","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"export default {\n  css: `\n    :host { display:block; width:100%; height:100%; }\n    .app { display:grid; grid-template-columns:340px 1fr; height:100%; min-height:0;\n           background:#0e0f13; color:#e8e8ee;\n           font-family:ui-sans-serif,-apple-system,\"Segoe UI\",Roboto,sans-serif; font-size:14px; }\n    .mono { font-family:ui-monospace,\"SF Mono\",Menlo,monospace }\n\n    aside { border-right:1px solid #23252d; display:flex; flex-direction:column; min-height:0; background:#0b0c10 }\n    .topo { padding:14px 16px 10px; border-bottom:1px solid #1a1c22 }\n    .topo h1 { font-size:14px; margin:0 0 2px; font-weight:650 }\n    .topo .s { font-size:11.5px; color:#7a7a86 }\n    .ctl { display:flex; gap:8px; align-items:center; padding:9px 16px; border-bottom:1px solid #1a1c22;\n           flex-wrap:wrap; font-size:12px; color:#8a8a96 }\n    .ctl input[type=search] { flex:1; min-width:104px; background:#151720; border:1px solid #2a2c35;\n           color:#e8e8ee; border-radius:6px; padding:5px 8px; font:inherit; font-size:12px }\n    .ctl label { display:flex; gap:5px; align-items:center; cursor:pointer; white-space:nowrap }\n    .lista { overflow:auto; flex:1; min-height:0 }\n    .it { padding:9px 16px; border-bottom:1px solid #15171d; cursor:pointer;\n          display:grid; grid-template-columns:1fr auto; gap:2px 8px }\n    .it:hover { background:#14161d }\n    .it.sel { background:#181d29; box-shadow:inset 3px 0 0 #5b8dd6 }\n    .it.viva { box-shadow:inset 3px 0 0 #3fbf76 }\n    .it.viva.sel { box-shadow:inset 3px 0 0 #7fe0a4; background:#14231b }\n    .it .cid { font-family:ui-monospace,Menlo,monospace; font-size:11.5px; overflow:hidden;\n               text-overflow:ellipsis; white-space:nowrap }\n    .it.viva .cid { color:#7fe0a4; font-weight:650 }\n    .it .meta { grid-column:1/-1; font-size:11px; color:#7a7a86; display:flex; gap:8px; flex-wrap:wrap;\n                align-items:center }\n    .it .hr { font-size:11px; color:#7a7a86; font-variant-numeric:tabular-nums }\n    .dot { width:7px; height:7px; border-radius:99px; background:#3a3c46; display:inline-block }\n    .dot.on { background:#3fbf76; box-shadow:0 0 7px #3fbf76 }\n\n    main { overflow:auto; min-height:0; padding:18px 22px 40px }\n    .vazio { color:#6a6a76; margin-top:60px; text-align:center }\n    h2 { font-size:17px; margin:0 0 3px; font-weight:650; word-break:break-all }\n    .sub { color:#8a8a96; font-size:12.5px }\n    .cards { display:grid; gap:12px; grid-template-columns:repeat(auto-fit,minmax(206px,1fr)); margin:16px 0 18px }\n    .card { background:#12141a; border:1px solid #23252d; border-radius:9px; padding:12px 14px }\n    .card h3 { font-size:10.5px; text-transform:uppercase; letter-spacing:.06em; color:#7a7a86;\n               margin:0 0 9px; font-weight:650 }\n    .kv { display:grid; grid-template-columns:auto 1fr; gap:3px 10px; font-size:12.5px; margin:0 }\n    .kv dt { color:#7a7a86 } .kv dd { margin:0; font-variant-numeric:tabular-nums; word-break:break-all }\n    .big { font-size:22px; font-weight:640; letter-spacing:-.02em; font-variant-numeric:tabular-nums }\n    .pill { display:inline-block; padding:2px 8px; border-radius:99px; font-size:11px; font-weight:650 }\n    .ok { background:#14301f; color:#7fe0a4 } .idle { background:#26262e; color:#8a8a96 }\n\n    .cmds { border:1px solid #23252d; border-radius:9px; padding:14px; background:#12141a; margin-bottom:16px }\n    .cmds h3 { font-size:10.5px; text-transform:uppercase; letter-spacing:.06em; color:#7a7a86;\n               margin:0 0 11px; font-weight:650 }\n    .row { display:flex; gap:8px; flex-wrap:wrap; align-items:center; margin-bottom:10px }\n    button { font:inherit; font-size:12.5px; padding:7px 13px; border-radius:7px; border:1px solid #2c2e37;\n             background:#191b22; color:#e8e8ee; cursor:pointer; white-space:nowrap }\n    button:hover { background:#22242d }\n    button.t { border-color:#7a5a1e; background:#2a2113; color:#f0c882 }\n    button.e { border-color:#6d2a26; background:#2a1614; color:#f0a49c }\n    button.r { border-color:#2c5a86; background:#132234; color:#9ec8f0 }\n    input[type=text] { background:#151720; border:1px solid #2a2c35; color:#e8e8ee; border-radius:6px;\n                       padding:6px 9px; font:inherit; font-size:12.5px }\n    textarea { width:100%; background:#151720; border:1px solid #2a2c35; color:#e8e8ee; border-radius:6px;\n               padding:8px 10px; font-family:ui-monospace,Menlo,monospace; font-size:12px; min-height:54px }\n    .hint { font-size:11.5px; color:#6a6a76 }\n    details summary { cursor:pointer; color:#8a8a96; font-size:12px; padding:4px 0 }\n    table { width:100%; border-collapse:collapse; font-size:12px }\n    td { padding:6px 10px 6px 0; border-bottom:1px solid #16181e; vertical-align:top; word-break:break-all }\n    .log { font-family:ui-monospace,Menlo,monospace; font-size:11.5px; color:#8a8a96;\n           max-height:220px; overflow:auto; border-top:1px solid #23252d; padding-top:10px; margin-top:6px }\n    .log div { padding:2px 0 }\n    .ln-ok { color:#7fe0a4 } .ln-err { color:#f0a49c }\n    .teclado { display:grid; grid-template-columns:repeat(6,minmax(0,54px)); gap:6px }\n    .tecla { font-size:15px; padding:9px 0; font-variant-numeric:tabular-nums }\n    .eventos { max-height:320px; overflow:auto; border-top:1px solid #23252d; padding-top:8px }\n    .portao { display:flex; align-items:center; justify-content:center; height:100%;\n              background:#0e0f13; color:#e8e8ee;\n              font-family:ui-sans-serif,-apple-system,\"Segoe UI\",Roboto,sans-serif }\n    .caixa { width:330px; background:#12141a; border:1px solid #23252d; border-radius:12px; padding:26px 24px }\n    .caixa h1 { font-size:16px; margin:0 0 3px; font-weight:650 }\n    .caixa input { width:100%; background:#151720; border:1px solid #2a2c35; color:#e8e8ee;\n                   border-radius:7px; padding:9px 11px; font:inherit; font-size:13px }\n    .chips { gap:6px; padding-top:7px; padding-bottom:8px }\n    .chip { font-size:11px; padding:3px 9px; border-radius:99px; border:1px solid #2a2c35;\n            background:#141620; color:#6a6a76; cursor:pointer; user-select:none }\n    .chip.on { background:#1c2333; border-color:#3a4a66; color:#cfe0f5 }\n    .tag { font-size:10px; padding:1px 6px; border-radius:4px; background:#22242d; color:#8a8a96 }\n    .tag.t-genesys { background:#1d2b3a; color:#9ec8f0 }\n    .tag.t-linphone { background:#2a2113; color:#f0c882 }\n    .tag.t-sipp { background:#14301f; color:#7fe0a4 }\n    .tag.t-55pbx { background:#2a1a2c; color:#d4a4e8 }\n    .escada { display:grid; grid-template-columns:86px 1fr; gap:0 12px; font-size:11.5px }\n    .escada .h { color:#7a7a86; font-family:ui-monospace,Menlo,monospace }\n    .escada .l { border-left:2px solid #23252d; padding:3px 0 3px 12px; position:relative }\n    .escada .l.out { color:#f0c882 } .escada .l.in { color:#9ec8f0 }\n    .visor { min-width:120px; padding:6px 10px; border:1px solid #2a2c35; border-radius:6px;\n             background:#151720; letter-spacing:.12em }\n    select { background:#151720; border:1px solid #2a2c35; color:#e8e8ee; border-radius:6px;\n             padding:5px 8px; font:inherit; font-size:12.5px }\n    .ev-linha { cursor:pointer }\n    .ev-linha:hover td { background:#191b24 }\n    pre.pacote { margin:0; padding:9px 11px; background:#101219; border:1px solid #23252d;\n                 border-radius:6px; font:11.5px/1.55 ui-monospace,SFMono-Regular,Menlo,monospace;\n                 color:#c9d6e4; white-space:pre; overflow:auto; max-height:360px }\n    pre.pacote.sip { color:#9ec8f0 }\n    .pacote-rot { font-size:10.5px; text-transform:uppercase; letter-spacing:.1em;\n                  color:#6a6a76; margin:0 0 5px }\n  `,\n\n  async mounted(d2) {\n    d2.setState({\n      todas: [], sel: null, det: null, logs: [],\n      busca: '', sovivas: false, auto: true, carregado: false,\n      intervalo: 20, stats: null, dtmf: '', metodoDtmf: 'audio',\n      meta: {}, tiposVis: { genesys: true, linphone: true, sipp: true, '55pbx': false, outro: false },\n      senha: (function () { try { return localStorage.getItem('ura-console-senha') || '' } catch (e) { return '' } })(),\n      negado: false,\n      cruAberto: false,\n      pacotes: {},\n      metodoTexto: 'INFO',\n      hdrsTexto: '{ \"Max-Forwards\": \"70\" }',\n      corpoTexto: '',\n      tipoConteudoTexto: 'application/sdp',\n      ddrTexto: d2.param('ddrPadrao', '+551148905654'),\n      // desligado por padrao: o ultimo Refer-To que o parceiro ditou nao tem user=phone.\n      userPhone: false,\n      // o REFER que funciona no trace da Wittel escapa os separadores do valor do\n      // U2U (%3B / %3D). O parceiro ditou a forma crua. Da para mandar as duas.\n      escaparU2u: false,\n      // pede ao telecom server o enderecamento in-dialog (Request-URI do remote\n      // target + Route: do route set + Contact). Vale para todos os comandos.\n      emDialogo: true,\n      // vazio = usa o host detectado no From da chamada. Persistido porque o\n      // dominio BYOC do parceiro e sempre o mesmo entre sessoes.\n      hostTexto: (function () { try { return localStorage.getItem('ura-console-host') || '' } catch (e) { return '' } })(),\n      u2uTexto: (function () { try { return localStorage.getItem('ura-console-u2u') || '' } catch (e) { return '' } })(),\n      referLivre: (function () { try { return localStorage.getItem('ura-console-refer-livre') || '' } catch (e) { return '' } })()\n    })\n    const comSenha = (o) => Object.assign({ senha: d2.state.senha || '' }, o || {})\n    const checaNegado = (out) => {\n      if (out && out.erro === 'acesso negado') { d2.setState({ negado: true }); return true }\n      return false\n    }\n\n    const refLive = d2.param('rushLive', 'poc-ura-live')\n    const refSinal = d2.param('rushSinal', 'poc-ura-signal')\n    const vivas = new Set()\n    let primeira = true\n\n    const escreve = (msg, cls) => {\n      const hora = new Date().toTimeString().slice(0, 8)\n      d2.setState({ logs: [{ hora, msg, cls }].concat(d2.state.logs || []).slice(0, 60) })\n    }\n\n    const puxarLista = async () => {\n      try {\n        const r = await d2.rush(refLive).invoke(comSenha({}))\n        const out = (r && r.data) ? r.data : r\n        const lista = (out.chamadas || []).slice()\n          .sort((a, b) => String(b.startTime || '').localeCompare(String(a.startTime || '')))\n        const agora = lista.filter(c => c.ia_ativa === true || c.ack === true || c.state === 'conversation_ready')\n        if (!primeira) agora.forEach(c => {\n          if (!vivas.has(c.callId)) escreve('chamada nova ' + c.callId + ' de ' + c.caller, 'ln-ok')\n        })\n        primeira = false\n        vivas.clear(); agora.forEach(c => vivas.add(c.callId))\n        d2.setState({ todas: lista, total: out.total, vivasTotal: agora.length, carregado: true })\n        // sem nada selecionado, abre sozinho na chamada viva mais recente\n        if (!d2.state.sel && agora.length) {\n          d2.setState({ sel: agora[0].callId, eventos: [], logsDe: null, logsCarregando: true })\n          puxarDetalhe(); puxarLogs()\n        }\n      } catch (e) { escreve('erro ao ler a borda: ' + e, 'ln-err') }\n    }\n\n    const puxarDetalhe = async () => {\n      if (!d2.state.sel) return\n      try {\n        const r = await d2.rush(refLive).invoke(comSenha({ callId: d2.state.sel }))\n        const out = (r && r.data) ? r.data : r\n        d2.setState({ det: (out.detalhe && out.detalhe.callId) ? out.detalhe : null })\n      } catch (e) { d2.setState({ det: null }) }\n    }\n\n    const puxarStats = async () => {\n      try {\n        const r = await d2.rush('poc-ura-ctrl').invoke(comSenha({ acao: 'stats' }))\n        const out = (r && r.data) ? r.data : r\n        if (checaNegado(out)) return\n        d2.setState({ stats: out.resposta || null, negado: false })\n      } catch (e) { /* silencioso: saude é acessório */ }\n    }\n\n    const puxarLogs = async () => {\n      const id = d2.state.sel\n      if (!id) { d2.setState({ eventos: [], logsDe: null }); return }\n      if (d2.state.logsDe !== id) d2.setState({ logsCarregando: true })\n      let desde\n      try {\n        const naLista = (d2.state.todas || []).find(c => c.callId === id) || {}\n        const inicio = (d2.state.det && d2.state.det.startTime) || naLista.startTime\n        desde = inicio\n          ? new Date(new Date(inicio).getTime() - 120000).toISOString()\n          : new Date(Date.now() - 3600000).toISOString()\n      } catch (e) { desde = new Date(Date.now() - 3600000).toISOString() }\n      try {\n        const r = await d2.rush('poc-ura-ctrl').invoke(comSenha({\n          acao: 'logs',\n          query: { callId: id, createdAt: { '$gte': desde }, '$limit': 300, '$sort': { timestamp: 1 } }\n        }))\n        // a seleção pode ter mudado enquanto a resposta vinha: descarta o atrasado\n        if (d2.state.sel !== id) return\n        const out = (r && r.data) ? r.data : r\n        const corpo = (out.resposta && out.resposta.data) ? out.resposta.data : (out.resposta || {})\n        d2.setState({\n          eventos: corpo.eventos || [],\n          logsDe: id,\n          logsCarregando: false,\n          logsErro: null,\n          logsEm: new Date().toTimeString().slice(0, 8)\n        })\n      } catch (e) {\n        if (d2.state.sel === id) d2.setState({\n          eventos: [], logsDe: id, logsCarregando: false, logsErro: String((e && e.message) || e)\n        })\n      }\n    }\n\n    const puxarChamadasLog = async () => {\n      try {\n        const desde = new Date(Date.now() - 36 * 3600000).toISOString()\n        const r = await d2.rush('poc-ura-ctrl').invoke(comSenha({ acao: 'logs', query: null, modoLog: 'chamadas', desde, limite: 200 }))\n        const out = (r && r.data) ? r.data : r\n        const corpo = (out.resposta && out.resposta.data) ? out.resposta.data : (out.resposta || {})\n        const mapa = {}\n        ;(corpo.chamadas || []).forEach(c => { mapa[c.callId] = c })\n        d2.setState({ meta: mapa })\n      } catch (e) { /* classificacao e acessoria */ }\n    }\n\n    const cmdDireto = async (extra) => {\n      const sel2 = d2.state.sel\n      if (!sel2) { escreve('nenhuma chamada selecionada', 'ln-err'); return }\n      escreve('enviando ' + (extra.sinal || '') + '…')\n      try {\n        const r = await d2.rush(refSinal).invoke(comSenha(Object.assign({ callId: sel2 }, extra)))\n        const out = (r && r.data) ? r.data : r\n        if (out.ok) escreve(out.metodo + ' enviado · ' + JSON.stringify(out.headers_enviados) +\n          (out.corpo_enviado ? ' · corpo: ' + String(out.corpo_enviado).replace(/\\n/g, ' ') : ''), 'ln-ok')\n        else escreve((out.metodo || extra.sinal) + ' falhou · http ' + (out.status_http || '?') +\n          ' · ' + (out.erro_servidor || out.erro || ''), 'ln-err')\n      } catch (err) { escreve('erro: ' + err, 'ln-err') }\n      puxarDetalhe(); puxarLogs()\n    }\n\n    const ctrl = async (corpo) => {\n      escreve('enviando ' + corpo.acao + '…')\n      try {\n        const r = await d2.rush('poc-ura-ctrl').invoke(comSenha(corpo))\n        const out = (r && r.data) ? r.data : r\n        if (checaNegado(out)) { escreve('acesso negado — confira a senha', 'ln-err'); return }\n        const resumo = JSON.stringify(out.resposta || {}).slice(0, 180)\n        if (out.ok) escreve(corpo.acao + ' ok · ' + resumo, 'ln-ok')\n        else escreve(corpo.acao + ' falhou · http ' + (out.status_http || '?') + ' · ' + resumo, 'ln-err')\n      } catch (err) { escreve('erro: ' + err, 'ln-err') }\n      puxarDetalhe(); puxarLista()\n    }\n\n    d2.on(d2.root, 'click', async (e) => {\n      const alvo = e.target\n      if (!alvo || !alvo.closest) return\n\n      const linha = alvo.closest('[data-call]')\n      if (linha) {\n        // limpa tudo da chamada anterior antes de carregar a nova\n        d2.setState({\n          sel: linha.dataset.call, det: null, logs: [],\n          eventos: [], logsDe: null, logsCarregando: true, logsEm: null, logsErro: null\n        })\n        puxarDetalhe(); puxarLogs()\n        return\n      }\n      const cru = alvo.closest('[data-cru]')\n      if (cru) { d2.setState({ cruAberto: !d2.state.cruAberto }); return }\n      const copiar = alvo.closest('[data-copiar]')\n      if (copiar) {\n        const celula = copiar.closest('td')\n        const bloco = celula && celula.querySelector('pre.pacote')\n        try {\n          navigator.clipboard.writeText(bloco ? bloco.textContent : '')\n          escreve('pacote copiado para a área de transferência', 'ln-ok')\n        } catch (err) { escreve('não deu para copiar: ' + err, 'ln-err') }\n        return\n      }\n      const u2uBtn = alvo.closest('[data-u2uhex]')\n      if (u2uBtn) {\n        const v = u2uBtn.dataset.u2uhex\n        d2.setState({ u2uTexto: v })\n        try { localStorage.setItem('ura-console-u2u', v) } catch (e) {}\n        return\n      }\n      const pac = alvo.closest('[data-key]')\n      if (pac) {\n        const chave = pac.dataset.key\n        const abertos = Object.assign({}, d2.state.pacotes || {})\n        if (abertos[chave]) { delete abertos[chave] } else { abertos[chave] = true }\n        d2.setState({ pacotes: abertos })\n        return\n      }\n      const entrar = alvo.closest('[data-entrar]')\n      if (entrar) {\n        const campo = d2.$('#senha')\n        const v = (campo && campo.value || '').trim()\n        try { localStorage.setItem('ura-console-senha', v) } catch (e) {}\n        d2.setState({ senha: v, negado: false })\n        puxarLista(); puxarStats(); puxarChamadasLog()\n        return\n      }\n      const sair = alvo.closest('[data-sair]')\n      if (sair) {\n        try { localStorage.removeItem('ura-console-senha') } catch (e) {}\n        d2.setState({ senha: '', negado: true, sel: null, todas: [], eventos: [] })\n        return\n      }\n      const agora = alvo.closest('[data-agora]')\n      if (agora) {\n        d2.setState({ atualizando: true })\n        await Promise.all([puxarLista(), puxarDetalhe(), puxarStats(), puxarLogs(), puxarChamadasLog()])\n        d2.setState({ atualizando: false })\n        return\n      }\n      const chip = alvo.closest('[data-tipo]')\n      if (chip) {\n        const t = chip.dataset.tipo\n        const vis = Object.assign({}, d2.state.tiposVis)\n        vis[t] = !vis[t]\n        d2.setState({ tiposVis: vis })\n        return\n      }\n      const tecla = alvo.closest('[data-dtmf]')\n      if (tecla) {\n        const d = tecla.dataset.dtmf\n        if (d === 'LIMPAR') d2.setState({ dtmf: '' })\n        else d2.setState({ dtmf: (d2.state.dtmf || '') + d })\n        return\n      }\n      const ct = alvo.closest('[data-ctrl]')\n      if (ct) {\n        const sel2 = d2.state.sel\n        const acao = ct.dataset.ctrl\n        if (!sel2 && acao !== 'stats' && acao !== 'outbound') {\n          escreve('nenhuma chamada selecionada', 'ln-err'); return\n        }\n        if (acao === 'dtmf') {\n          const seq = d2.state.dtmf || ''\n          if (!seq) { escreve('sequência DTMF vazia', 'ln-err'); return }\n          if (d2.state.metodoDtmf === 'info') {\n            cmdDireto({ sinal: 'INFO_DTMF', sequence: seq })\n            return\n          }\n          ctrl({ acao: 'dtmf', callId: sel2, sequence: seq, metodoDtmf: d2.state.metodoDtmf })\n        } else if (acao === 'hangup' || acao === 'hangup_force') {\n          ctrl({ acao: 'hangup', callId: sel2, reason: 'console_manual', force: acao === 'hangup_force' })\n        } else {\n          ctrl({ acao: acao, callId: sel2 })\n        }\n        return\n      }\n      const btn = alvo.closest('[data-cmd]')\n      if (!btn) return\n\n      const sel = d2.state.sel\n      if (!sel) { escreve('nenhuma chamada selecionada', 'ln-err'); return }\n\n      let corpo\n      let alvoRush = refSinal\n      const tipo = btn.dataset.cmd\n      if (tipo === 'REFER_URA') {\n        // caminho de producao: o MESMO listener que o caso de uso \"falar com\n        // atendente\" chama no entrypoint. Campo vazio = valor que a URA usa\n        // sozinha, entao deixar tudo em branco testa a producao exata.\n        alvoRush = 'poc-ura-transferir'\n        // debug: o listener devolve o pacote SIP e a request-uri; sem essa flag\n        // ele responde magro, do jeito que o agente de voz recebe em producao\n        corpo = { debug: true }\n        const livre = (d2.state.referLivre || '').trim()\n        if (livre) {\n          corpo.refer_to = livre.replace(/^</, '').replace(/>$/, '')\n        } else {\n          if ((d2.state.ddrTexto || '').trim()) corpo.numero = d2.state.ddrTexto.trim()\n          if ((d2.state.hostTexto || '').trim()) corpo.host = d2.state.hostTexto.trim()\n          if ((d2.state.u2uTexto || '').trim()) corpo.u2u = d2.state.u2uTexto.trim()\n          corpo.user_phone = !!d2.state.userPhone\n          corpo.escapar = !!d2.state.escaparU2u\n        }\n      } else if (tipo === 'REFER') {\n        // user=phone e parametro de URI: declara que a parte de usuario e um numero\n        // E.164 discavel, nao um usuario SIP. Vai DENTRO dos brackets, depois do host.\n        corpo = { sinal: 'REFER', referTo: montaReferTo(d2, hostSbc(d2)) }\n      } else if (tipo === 'RAW') {\n        let headers\n        try { headers = JSON.parse(d2.state.hdrsTexto || '{}') }\n        catch (err) { escreve('JSON de headers inválido', 'ln-err'); return }\n        corpo = { sinal: 'RAW', metodo: (d2.state.metodoTexto || 'INFO').toUpperCase(), headers }\n        if ((d2.state.corpoTexto || '').trim()) {\n          corpo.corpoSip = d2.state.corpoTexto\n          corpo.contentType = d2.state.tipoConteudoTexto || 'application/sdp'\n        }\n      } else {\n        corpo = JSON.parse(btn.dataset.args || '{}')\n      }\n\n      escreve('enviando ' + (corpo.metodo || corpo.sinal || 'REFER da URA') + '…')\n      try {\n        // in-dialog e o default nos listeners; so mandamos chave quando o\n        // operador desliga (igualdade contra false nao e confiavel no d2lang)\n        const base = { callId: sel }\n        if (d2.state.emDialogo === false) base.legado = true\n        const r = await d2.rush(alvoRush).invoke(comSenha(Object.assign(base, corpo)))\n        const out = (r && r.data) ? r.data : r\n        if (out.ok) escreve((out.metodo || 'REFER') + ' enviado'\n          + (out.request_uri ? ' · para ' + out.request_uri : '')\n          + (out.dialogo && out.dialogo.routeSetSize ? ' · ' + out.dialogo.routeSetSize + ' Route' : '')\n          + ' · ' + JSON.stringify(out.headers_enviados) + ' · ' + (out.destino || ''), 'ln-ok')\n        else escreve((out.metodo || corpo.sinal || 'REFER') + ' falhou · http ' + (out.status_http || '?') + ' ' + (out.erro_servidor || out.erro || ''), 'ln-err')\n      } catch (err) { escreve('erro: ' + err, 'ln-err') }\n      puxarDetalhe()\n    })\n\n    d2.on(d2.root, 'input', (e) => {\n      const el = e.target\n      if (!el || !el.dataset) return\n      if (el.dataset.filtro) { d2.setState({ busca: el.value }); return }\n      // campos do painel de comandos vivem no state, senao o refresh os apaga\n      if (el.dataset.campo) {\n        const p = {}; p[el.dataset.campo] = el.value\n        d2.setState(p)\n        const guardados = {\n          hostTexto: 'ura-console-host',\n          u2uTexto: 'ura-console-u2u',\n          referLivre: 'ura-console-refer-livre'\n        }\n        if (guardados[el.dataset.campo]) {\n          try { localStorage.setItem(guardados[el.dataset.campo], el.value) } catch (e) {}\n        }\n      }\n    })\n    d2.on(d2.root, 'change', (e) => {\n      const el = e.target\n      if (!el || !el.dataset || !el.dataset.flag) return\n      const p = {}; p[el.dataset.flag] = el.checked\n      d2.setState(p)\n    })\n\n    d2.on(d2.root, 'change', (e) => {\n      const el = e.target\n      if (!el || !el.dataset) return\n      if (el.dataset.intervalo != null) d2.setState({ intervalo: Number(el.value) })\n      if (el.dataset.metodoDtmf != null) d2.setState({ metodoDtmf: el.value })\n    })\n\n    await puxarLista(); await puxarStats(); puxarChamadasLog()\n    const tm = setInterval(() => { if (d2.state.auto) puxarChamadasLog() }, 60000)\n    d2.onUnmount(() => clearInterval(tm))\n    let passou = 0\n    const t = setInterval(() => {\n      if (!d2.state.auto) return\n      passou++\n      if (passou < (d2.state.intervalo || 20)) return\n      passou = 0\n      const foco = d2.root.querySelector && d2.root.querySelector('input:focus, textarea:focus')\n      if (foco) return\n      puxarLista(); puxarDetalhe(); puxarStats()\n    }, 1000)\n    // o log da borda tem ritmo próprio: é barato (filtrado por callId) e precisa parecer vivo\n    const tl = setInterval(() => {\n      if (!d2.state.auto || !d2.state.sel) return\n      // mesma guarda do refresh geral: nao re-renderiza enquanto o usuario digita,\n      // senao o painel de comando cru fecha e o texto volta ao padrao no meio\n      const foco = d2.root.querySelector && d2.root.querySelector('input:focus, textarea:focus, select:focus')\n      if (foco) return\n      puxarLogs()\n    }, 4000)\n    d2.onUnmount(() => { clearInterval(t); clearInterval(tl) })\n  },\n\n  render(d2) {\n    const esc = (s) => String(s == null ? '' : s).replace(/[<>&]/g, c => ({ '<': '&lt;', '>': '&gt;', '&': '&amp;' }[c]))\n\n    if (!d2.state.senha || d2.state.negado) {\n      return `<div class=\"portao\">\n        <div class=\"caixa\">\n          <h1>Console da URA de voz</h1>\n          <div class=\"sub\" style=\"margin-bottom:16px\">natura-br.data2.tel · acesso restrito</div>\n          ${d2.state.negado && d2.state.senha ? '<div class=\"ln-err\" style=\"font-size:12.5px;margin-bottom:10px\">senha incorreta</div>' : ''}\n          <input type=\"password\" id=\"senha\" placeholder=\"senha de acesso\" autocomplete=\"current-password\">\n          <button class=\"r\" data-entrar=\"1\" style=\"margin-top:10px;width:100%\">entrar</button>\n          <div class=\"hint\" style=\"margin-top:14px;line-height:1.5\">Este console envia comandos SIP para\n            chamadas em produção. Não compartilhe a senha fora do time de integração.</div>\n        </div>\n      </div>`\n    }\n    const viva = (c) => c.ia_ativa === true || c.ack === true || c.state === 'conversation_ready'\n    const todas = d2.state.todas || []\n    const sel = d2.state.sel\n    const det = d2.state.det\n    const ddr = d2.param('ddrPadrao', '+551148905654')\n\n    const meta = d2.state.meta || {}\n    const classifica = (ua) => {\n      const u = String(ua || '')\n      if (/GENESYS/i.test(u)) return 'genesys'\n      if (/Linphone/i.test(u)) return 'linphone'\n      if (/SIPp/i.test(u)) return 'sipp'\n      if (/55PBX/i.test(u)) return '55pbx'\n      return 'outro'\n    }\n    const tipoDe = (id) => classifica((meta[id] || {}).user_agent)\n    const vis = d2.state.tiposVis || {}\n    const rotulos = { genesys: 'Genesys', linphone: 'Linphone', sipp: 'SIPp', '55pbx': '55PBX', outro: 'outros' }\n\n    const contagem = { genesys: 0, linphone: 0, sipp: 0, '55pbx': 0, outro: 0 }\n    todas.forEach(c => { contagem[tipoDe(c.callId)]++ })\n\n    let lista = d2.state.sovivas ? todas.filter(viva) : todas\n    const antesDoTipo = lista.length\n    lista = lista.filter(c => vis[tipoDe(c.callId)] === true)\n    const ocultadas = antesDoTipo - lista.length\n    const q = (d2.state.busca || '').toLowerCase()\n    if (q) lista = lista.filter(c => (c.callId + ' ' + c.caller).toLowerCase().includes(q))\n\n    const esquerda = `\n      <aside>\n        <div class=\"topo\">\n          <h1>natura-br.data2.tel</h1>\n          <div class=\"s\">${d2.state.carregado\n            ? `${d2.fmt.number(d2.state.total || 0)} na borda · ${d2.state.vivasTotal || 0} viva${d2.state.vivasTotal === 1 ? '' : 's'}`\n            : 'carregando…'}</div>\n          ${d2.state.stats ? `<div class=\"s\" style=\"margin-top:3px\">servidor ok · ${d2.state.stats.activeCalls} ativas · ${d2.state.stats.comMidia} com mídia · ${d2.state.stats.comIa} com assistente</div>` : ''}\n        </div>\n        <div class=\"ctl\">\n          <input type=\"search\" data-filtro=\"1\" placeholder=\"filtrar call_id / número\" value=\"${esc(d2.state.busca)}\">\n          <label><input type=\"checkbox\" data-flag=\"sovivas\" ${d2.state.sovivas ? 'checked' : ''}> só vivas</label>\n        </div>\n        <div class=\"ctl chips\">\n          ${Object.keys(rotulos).map(t => `<span class=\"chip ${vis[t] ? 'on' : ''}\" data-tipo=\"${t}\">${rotulos[t]} ${contagem[t] || 0}</span>`).join('')}\n          <span class=\"chip on\" data-agora=\"1\" title=\"atualizar tudo agora\">${d2.state.atualizando ? '↻…' : '↻ atualizar'}</span>\n          <span class=\"chip\" data-sair=\"1\" title=\"sair\">sair</span>\n          <label><input type=\"checkbox\" data-flag=\"auto\" ${d2.state.auto ? 'checked' : ''}> auto</label>\n          <select data-intervalo=\"1\">\n            ${[10, 20, 30, 60, 120].map(n => `<option value=\"${n}\"${(d2.state.intervalo || 20) === n ? ' selected' : ''}>${n}s</option>`).join('')}\n          </select>\n        </div>\n        <div class=\"lista\">\n          ${lista.length ? lista.slice(0, 200).map(c => `\n            <div class=\"it ${viva(c) ? 'viva' : ''} ${c.callId === sel ? 'sel' : ''}\"\n                 data-call=\"${esc(c.callId)}\" data-key=\"${esc(c.callId)}\">\n              <div class=\"cid\">${esc(c.callId)}</div>\n              <div class=\"hr\">${String(c.startTime || '').slice(11, 19)}</div>\n              <div class=\"meta\"><span class=\"dot ${viva(c) ? 'on' : ''}\"></span>\n                <span class=\"tag t-${tipoDe(c.callId)}\">${esc(rotulos[tipoDe(c.callId)])}</span>\n                <span>${esc(c.caller || '—')}</span><span>${esc(c.state || '—')}</span>\n                <span>${Math.round((c.duration_ms || 0) / 1000)}s</span></div>\n            </div>`).join('')\n            : `<div style=\"padding:16px\" class=\"hint\">${ocultadas\n                ? ocultadas + ' chamada' + (ocultadas === 1 ? '' : 's') + ' ocultada' + (ocultadas === 1 ? '' : 's') + ' pelos filtros de origem — ligue um chip acima'\n                : 'nada aqui'}</div>`}\n        </div>\n      </aside>`\n\n    if (!sel) {\n      return `<div class=\"app\">${esquerda}<main><div class=\"vazio\">Selecione uma chamada à esquerda.</div></main></div>`\n    }\n\n    const naLista = todas.find(c => c.callId === sel) || {}\n    const h = (det && det.headers) || {}\n    const rtp = (det && det.rtpStats) || {}\n    const uuiRaw = h['user-to-user'] || h['User-to-User'] || ''\n    let uui = null\n    const hex = String(uuiRaw).split(';')[0]\n    if (/^[0-9a-fA-F]+$/.test(hex) && hex.length > 8) {\n      let s = ''\n      for (let i = 0; i < hex.length; i += 2) s += String.fromCharCode(parseInt(hex.substr(i, 2), 16))\n      const p = s.replace(/^\u0000/, '').split(';')\n      uui = { telA: p[0] || '', telB: p[1] || '', consultora: p[2] || '' }\n    }\n    const host = hostSbcDe(det)\n    const privado = (ip) => /^(10\\.|127\\.|0\\.0\\.0\\.0|192\\.168\\.|172\\.(1[6-9]|2\\d|3[01])\\.)/.test(String(ip || ''))\n    const ipDoVia = (v) => { const m = String(v || '').match(/SIP\\/2\\.0\\/[A-Z]+\\s+([0-9.]+)/); return m ? m[1] : '' }\n    const ipDoContato = (c) => { const m = String(c || '').match(/@([0-9.]+)/); return m ? m[1] : '' }\n    const mi = meta[sel] || {}\n    const ipVia = ipDoVia(mi.via), ipCt = ipDoContato(mi.contact), ipReal = mi.ip_origem\n    const temNat = (ipVia && privado(ipVia)) || (ipCt && privado(ipCt))\n    const natDiag = !mi.via ? '—'\n      : temNat\n        ? `<span style=\"color:#f0c882\">sim — anuncia privado, chega de ${esc(ipReal)}</span>`\n        : (ipVia && ipReal && ipVia !== ipReal\n            ? `<span style=\"color:#f0c882\">provável — Via ${esc(ipVia)} ≠ origem ${esc(ipReal)}</span>`\n            : '<span style=\"color:#7fe0a4\">não — endereços batem</span>')\n\n    const botao = (cls, rotulo, args) =>\n      `<button class=\"${cls}\" data-cmd=\"X\" data-args='${JSON.stringify(args)}'>${rotulo}</button>`\n\n    return `\n    <div class=\"app\">\n      ${esquerda}\n      <main>\n        <h2 class=\"mono\">${esc(sel)}</h2>\n        <div class=\"sub\">${det ? 'na borda' : 'não está mais na borda (encerrada)'}\n          ${naLista.caller ? ' · de ' + esc(naLista.caller) : ''}${det && det.to ? ' → ' + esc(det.to) : ''}</div>\n\n        <div class=\"cards\">\n          <div class=\"card\"><h3>Estado</h3>\n            <div class=\"big\">${esc((det && det.state) || naLista.state || '—')}</div>\n            <div style=\"margin-top:8px\">\n              <span class=\"pill ${det && det.ia_ativa ? 'ok' : 'idle'}\">assistente ${det && det.ia_ativa ? 'ativo' : '—'}</span>\n              <span class=\"pill ${det && det.ackReceived ? 'ok' : 'idle'}\">ACK ${det && det.ackReceived ? 'sim' : '—'}</span>\n            </div></div>\n\n          <div class=\"card\"><h3>Duração</h3>\n            <div class=\"big\">${Math.round((det && det.duration != null) ? det.duration : (naLista.duration_ms || 0) / 1000)}s</div>\n            <div class=\"hint\" style=\"margin-top:6px\">início ${String((det && det.startTime) || naLista.startTime || '').slice(11, 19)}</div></div>\n\n          <div class=\"card\"><h3>Consultora (do U2U)</h3>\n            <div class=\"big\">${uui ? esc(uui.consultora || '—') : '—'}</div>\n            <div class=\"hint\" style=\"margin-top:6px\">${uui ? esc(uui.telA) + ' · ' + esc(uui.telB) : 'sem U2U'}</div></div>\n\n          <div class=\"card\"><h3>Mídia</h3>\n            <dl class=\"kv\">\n              <dt>codec</dt><dd>${esc((det && det.codec && (det.codec.name || det.codec)) || naLista.codec || '—')}</dd>\n              <dt>rtp in</dt><dd>${esc(rtp.packetsReceived != null ? rtp.packetsReceived : '—')}</dd>\n              <dt>bytes in</dt><dd>${esc(rtp.bytesReceived != null ? rtp.bytesReceived : '—')}</dd>\n            </dl></div>\n\n          <div class=\"card\"><h3>Diagnóstico</h3>\n            <dl class=\"kv\">\n              <dt>origem</dt><dd><span class=\"tag t-${tipoDe(sel)}\">${esc(rotulos[tipoDe(sel)])}</span></dd>\n              <dt>user-agent</dt><dd>${esc((meta[sel] || {}).user_agent || (det && det.userAgent) || '—')}</dd>\n              <dt>IP real</dt><dd class=\"mono\">${esc((meta[sel] || {}).ip_origem || '—')}</dd>\n              <dt>Via anuncia</dt><dd class=\"mono\">${esc(ipDoVia((meta[sel] || {}).via) || '—')}</dd>\n              <dt>Contact</dt><dd class=\"mono\">${esc(ipDoContato((meta[sel] || {}).contact) || '—')}</dd>\n              <dt>NAT</dt><dd>${natDiag}</dd>\n              <dt>hops (RR)</dt><dd>${esc((meta[sel] || {}).hops != null ? (meta[sel] || {}).hops : '—')}</dd>\n              <dt>autenticado</dt><dd>${(meta[sel] || {}).autenticado ? 'sim' : '—'}</dd>\n            </dl></div>\n\n          <div class=\"card\"><h3>Sinalização</h3>\n            <dl class=\"kv\">\n              <dt>CSeq</dt><dd>${esc(JSON.stringify((det && det.cseqCounters) || {}))}</dd>\n              <dt>SBC</dt><dd class=\"mono\">${esc(host)}</dd>\n              <dt>user-agent</dt><dd>${esc((det && det.userAgent) || '—')}</dd>\n            </dl></div>\n        </div>\n\n        <div class=\"cmds\">\n          <h3>Comandos SIP para esta chamada</h3>\n          <div class=\"row\">\n            <button class=\"t\" data-cmd=\"X\" data-args='{\"sinal\":\"TRANSFER\"}'>INFO · TRANSFER</button>\n            <button class=\"e\" data-cmd=\"X\" data-args='{\"sinal\":\"END\"}'>INFO · END</button>\n            <button class=\"e\" data-cmd=\"X\" data-args='{\"sinal\":\"BYE\",\"cause\":\"480\"}'>BYE 480</button>\n            <button class=\"e\" data-cmd=\"X\" data-args='{\"sinal\":\"BYE\",\"cause\":\"404\",\"texto\":\"Not Found\"}'>BYE 404</button>\n            <button class=\"e\" data-cmd=\"X\" data-args='{\"sinal\":\"BYE\",\"cause\":\"503\",\"texto\":\"Service Unavailable\"}'>BYE 503</button>\n            <button class=\"e\" data-cmd=\"X\" data-args='{\"sinal\":\"BYE_Q850\",\"cause\":\"41\"}'>BYE Q.850 41</button>\n          </div>\n          <div class=\"row\">\n            <button data-cmd=\"X\" data-args='{\"sinal\":\"OPTIONS\"}' title=\"prova se o outro lado reconhece nosso diálogo\">OPTIONS · sondar</button>\n            <button data-cmd=\"X\" data-args='{\"sinal\":\"UPDATE\"}' title=\"refresh de sessão\">UPDATE</button>\n            <button data-cmd=\"X\" data-args='{\"sinal\":\"NOTIFY\",\"evento\":\"refer\"}' title=\"NOTIFY Event: refer\">NOTIFY refer</button>\n            <button class=\"e\" data-cmd=\"X\" data-args='{\"sinal\":\"CANCEL\"}' title=\"só vale em INVITE não atendido — em chamada estabelecida é inválido\">CANCEL ⚠</button>\n          </div>\n          <div class=\"row\">\n            <button class=\"r\" data-cmd=\"X\" title=\"re-INVITE no diálogo existente, sem SDP\"\n              data-args='{\"sinal\":\"RAW\",\"metodo\":\"INVITE\",\"headers\":{\"Contact\":\"<sip:server@18.229.6.249:5060>\",\"Allow\":\"INVITE, ACK, CANCEL, BYE, OPTIONS, INFO, UPDATE, REFER, NOTIFY\",\"Supported\":\"timer, 100rel\",\"Max-Forwards\":\"70\"}}'>re-INVITE</button>\n            <button class=\"r\" data-cmd=\"X\" title=\"re-INVITE com SDP (oferta de mídia)\"\n              data-args='{\"sinal\":\"RAW\",\"metodo\":\"INVITE\",\"contentType\":\"application/sdp\",\"corpoSip\":\"v=0\\r\\no=data2 53655765 2353687638 IN IP4 18.229.6.249\\r\\ns=-\\r\\nc=IN IP4 18.229.6.249\\r\\nt=0 0\\r\\nm=audio 10001 RTP/AVP 8 0 101\\r\\na=rtpmap:8 PCMA/8000\\r\\na=rtpmap:0 PCMU/8000\\r\\na=rtpmap:101 telephone-event/8000\\r\\na=fmtp:101 0-15\\r\\na=sendrecv\\r\\n\",\"headers\":{\"Contact\":\"<sip:server@18.229.6.249:5060>\",\"Allow\":\"INVITE, ACK, CANCEL, BYE, OPTIONS, INFO, UPDATE, REFER, NOTIFY\",\"Supported\":\"timer, 100rel\",\"Max-Forwards\":\"70\"}}'>re-INVITE + SDP</button>\n          </div>\n          <div class=\"row\">\n            <button class=\"r\" data-cmd=\"REFER\">REFER →</button>\n            <input type=\"text\" data-campo=\"ddrTexto\" value=\"${esc(d2.state.ddrTexto || ddr)}\" size=\"16\" placeholder=\"número\">\n            <span class=\"hint\">@</span>\n            <input type=\"text\" data-campo=\"hostTexto\" value=\"${esc(d2.state.hostTexto || '')}\" size=\"30\"\n              placeholder=\"${esc(host)} (da chamada)\">\n            <label class=\"hint\" style=\"display:flex;gap:5px;align-items:center;cursor:pointer\">\n              <input type=\"checkbox\" data-flag=\"userPhone\" ${d2.state.userPhone ? 'checked' : ''}> user=phone</label>\n            <label class=\"hint\" style=\"display:flex;gap:5px;align-items:center;cursor:pointer\"\n              title=\"escapa ; e = do valor do U2U — a forma que aparece no REFER que funciona\">\n              <input type=\"checkbox\" data-flag=\"escaparU2u\" ${d2.state.escaparU2u ? 'checked' : ''}> escapar %3B/%3D</label>\n          </div>\n          <div class=\"row\">\n            <span class=\"hint\" style=\"min-width:104px\">User-to-User</span>\n            <input type=\"text\" data-campo=\"u2uTexto\" value=\"${esc(d2.state.u2uTexto || '')}\" size=\"44\"\n              placeholder=\"hex do U2U — vai embutido na URI; ;encoding=hex entra sozinho se faltar\">\n            <span class=\"chip\" data-u2uhex=\"007472616e736665726972\" style=\"cursor:pointer\"\n              title=\"regra do cliente: UUI só diz o comando\">transferir</span>\n            <span class=\"chip\" data-u2uhex=\"006465736c69676172\" style=\"cursor:pointer\"\n              title=\"regra do cliente: UUI só diz o comando\">desligar</span>\n          </div>\n          ${(d2.state.u2uTexto || '').trim() ? `<div class=\"row\" style=\"margin-top:-5px\">\n            <span class=\"hint\" style=\"min-width:104px\"></span>\n            <span class=\"hint\">esse hex diz <span class=\"mono\" style=\"color:#c9a86a\">${esc(decodeHex(d2.state.u2uTexto))}</span></span>\n          </div>` : ''}\n          <div class=\"row\">\n            <span class=\"hint\" style=\"min-width:104px\">Refer-To livre</span>\n            <input type=\"text\" data-campo=\"referLivre\" value=\"${esc(d2.state.referLivre || '')}\" size=\"60\"\n              placeholder=\"cole a URI exata do parceiro — manda por cima dos campos acima\">\n          </div>\n          <div class=\"row\" style=\"margin-top:-4px\">\n            <span class=\"hint\">vai sair como <span class=\"mono\" style=\"color:#9ec8f0\">Refer-To: &lt;${esc(montaReferTo(d2, host))}&gt;</span>${(d2.state.referLivre || '').trim()\n              ? ' · do campo livre, sem remontar nada'\n              : ((d2.state.hostTexto || '').trim() ? '' : ' · host vindo do From da chamada')}</span>\n          </div>\n          <div class=\"row\">\n            <button class=\"r\" data-cmd=\"REFER_URA\">REFER como a URA manda →</button>\n            <label class=\"hint\" style=\"display:flex;gap:5px;align-items:center;cursor:pointer\"\n              title=\"Pede Request-URI do remote target + Route: do route set + Contact. Vale para TODOS os comandos desta tela. Só tem efeito com o PR #923 no ar.\">\n              <input type=\"checkbox\" data-flag=\"emDialogo\" ${d2.state.emDialogo !== false ? 'checked' : ''}> in-dialog</label>\n            <span class=\"hint\">mesmo listener que o caso de uso “falar com atendente” chama em produção.\n              Campos acima vazios = valores que a URA usa sozinha</span>\n          </div>\n          <div class=\"row\" style=\"border-top:1px solid #23252d;padding-top:11px\">\n            <button data-ctrl=\"hangup\">encerrar (/hangup)</button>\n            <button class=\"e\" data-ctrl=\"hangup_force\">encerrar forçado</button>\n            <button data-ctrl=\"cleanup\">limpar zumbi (/cleanup)</button>\n            <button data-ctrl=\"transfer_status\">status de transferência</button>\n          </div>\n\n          <div style=\"border-top:1px solid #23252d;padding-top:11px;margin-top:2px\">\n            <div class=\"hint\" style=\"margin-bottom:7px\">Teclado DTMF — a sequência vai por áudio (ou RFC 2833)</div>\n            <div class=\"teclado\">\n              ${['1','2','3','4','5','6','7','8','9','*','0','#'].map(t =>\n                `<button class=\"tecla\" data-dtmf=\"${t}\">${t}</button>`).join('')}\n            </div>\n            <div class=\"row\" style=\"margin-top:9px\">\n              <span class=\"visor mono\">${esc(d2.state.dtmf || '—')}</span>\n              <button data-dtmf=\"LIMPAR\">limpar</button>\n              <select data-metodo-dtmf=\"1\">\n                <option value=\"audio\"${d2.state.metodoDtmf === 'audio' ? ' selected' : ''}>áudio</option>\n                <option value=\"rfc2833\"${d2.state.metodoDtmf === 'rfc2833' ? ' selected' : ''}>RFC 2833</option>\n                <option value=\"info\"${d2.state.metodoDtmf === 'info' ? ' selected' : ''}>SIP INFO (dtmf-relay)</option>\n              </select>\n              <button class=\"t\" data-ctrl=\"dtmf\">enviar DTMF</button>\n            </div>\n          </div>\n\n          <div style=\"margin-top:10px;border-top:1px solid #23252d;padding-top:9px\">\n            <div class=\"hint\" data-cru=\"1\" style=\"cursor:pointer;user-select:none\">\n              ${d2.state.cruAberto ? '▾' : '▸'} comando cru (qualquer método SIP)\n            </div>\n            ${d2.state.cruAberto ? `\n            <div class=\"row\" style=\"margin-top:9px\">\n              <input type=\"text\" data-campo=\"metodoTexto\" value=\"${esc(d2.state.metodoTexto)}\" size=\"8\" placeholder=\"método\">\n              <input type=\"text\" data-campo=\"tipoConteudoTexto\" value=\"${esc(d2.state.tipoConteudoTexto)}\" size=\"22\" placeholder=\"content-type (se houver corpo)\">\n              <button data-cmd=\"RAW\">enviar</button>\n            </div>\n            <div class=\"hint\" style=\"margin:2px 0 4px\">headers em JSON</div>\n            <textarea data-campo=\"hdrsTexto\" spellcheck=\"false\">${esc(d2.state.hdrsTexto)}</textarea>\n            <div class=\"hint\" style=\"margin:7px 0 4px\">corpo (opcional — SDP, dtmf-relay, etc.)</div>\n            <textarea data-campo=\"corpoTexto\" spellcheck=\"false\" placeholder=\"vazio = sem corpo\">${esc(d2.state.corpoTexto)}</textarea>\n            <div class=\"hint\" style=\"margin-top:6px\">Métodos aceitos hoje: BYE, INFO, OPTIONS, CANCEL, UPDATE, REFER,\n              NOTIFY, SUBSCRIBE, PRACK — e INVITE quando o servidor for atualizado.</div>\n            ` : ''}\n          </div>\n        </div>\n\n        ${Object.keys(h).length ? `<details><summary>headers do INVITE (${Object.keys(h).length})</summary>\n          <table>${Object.keys(h).map(k => `<tr>\n            <td style=\"color:#7a7a86;white-space:nowrap\">${esc(k)}</td>\n            <td class=\"mono\">${esc(Array.isArray(h[k]) ? h[k].join('  |  ') : h[k])}</td></tr>`).join('')}</table>\n          </details>` : ''}\n\n        <div class=\"cmds\">\n          <h3><span class=\"chip on\" data-agora=\"1\" style=\"margin-right:8px;text-transform:none;letter-spacing:0\">↻ agora</span>Log da borda\n            <span class=\"hint\" style=\"text-transform:none;letter-spacing:0;font-weight:400\">\n              ${d2.state.logsCarregando ? '· carregando…'\n                : `· ${(d2.state.eventos || []).length} eventos${d2.state.logsEm ? ' · ' + d2.state.logsEm : ''}${d2.state.auto ? ' · <span style=\"color:#7fe0a4\">● ao vivo (4s)</span>' : ' · auto desligado'}`}\n              ${d2.state.logsErro ? `<span class=\"ln-err\"> · ${esc(d2.state.logsErro)}</span>` : ''}\n              ${d2.state.logsDe ? `<span style=\"color:#4a4a56\"> · de ${esc(String(d2.state.logsDe).slice(0, 22))}</span>` : ''}\n            </span>\n          </h3>\n          ${(d2.state.eventos || []).length ? `<div class=\"escada\" style=\"margin-bottom:12px\">\n            ${(d2.state.eventos || []).filter(ev => ev.direcao === 'in' || ev.direcao === 'out').map(ev => `\n              <div class=\"h\">${esc(String(ev.quando || '').slice(11, 19))}</div>\n              <div class=\"l ${esc(ev.direcao)}\">${ev.direcao === 'in' ? '→ recebemos' : '← enviamos'}\n                <b>${esc(ev.metodo || ev.tipo)}</b>\n                ${ev.motivo ? '· ' + esc(ev.motivo) : ''}</div>`).join('')}\n          </div>` : ''}\n\n          <div class=\"eventos\">\n            ${(d2.state.eventos || []).length\n              ? `<table>${(d2.state.eventos || []).map(ev => { const pk = String((ev.ts || ev.quando) + '|' + ev.tipo); const aberto = !!((d2.state.pacotes || {})[pk]); return `<tr class=\"ev-linha\" data-key=\"${esc(pk)}\" title=\"clique para abrir o pacote\">\n                  <td class=\"mono\" style=\"white-space:nowrap;color:#7a7a86\">${esc(String(ev.quando || '').slice(11, 23))}</td>\n                  <td style=\"white-space:nowrap\"><span style=\"color:#5a5a66\">${aberto ? '▾' : '▸'}</span> ${esc(ev.tipo)}</td>\n                  <td style=\"color:#7a7a86;white-space:nowrap\">${esc(ev.direcao || '')} ${esc(ev.metodo || '')} ${esc(ev.motivo || '')}</td>\n                  <td class=\"mono\" style=\"font-size:11px;color:#6a6a76\">${ev.sip ? esc(String(ev.sip).split('\\r\\n')[0]) : ''}</td>\n                </tr>${aberto ? `<tr><td colspan=\"4\" style=\"padding:2px 0 14px\">\n                  <div class=\"pacote-rot\">pacote cru<span class=\"chip\" data-copiar=\"1\" style=\"margin-left:8px;text-transform:none;letter-spacing:0;cursor:pointer\">copiar</span></div>\n                  <pre class=\"pacote\">${esc(pacoteTexto(ev))}</pre>\n                  ${sipTexto(ev) ? `<div class=\"pacote-rot\" style=\"margin-top:9px\">mensagem SIP</div><pre class=\"pacote sip\">${esc(sipTexto(ev))}</pre>` : ''}\n                </td></tr>` : ''}` }).join('')}</table>`\n              : (d2.state.logsCarregando ? '<div class=\"hint\">buscando eventos desta chamada…</div>'\n                                          : '<div class=\"hint\">nenhum evento nesta janela</div>')}\n          </div>\n        </div>\n\n        <div class=\"hint\" style=\"margin-top:14px\">console de comandos — histórico do que você disparou (limpa ao trocar de chamada)</div>\n        <div class=\"log\">${(d2.state.logs || []).map(l =>\n          `<div class=\"${l.cls || ''}\">${esc(l.hora)}  ${esc(l.msg)}</div>`).join('')}</div>\n      </main>\n    </div>`\n  }\n}\n\n// o servidor ja ofusca o motor de voz; aqui e a segunda barreira, caso um campo\n// novo apareca no log antes de o handler ser atualizado.\nfunction limpaMotor(txt) {\n  return String(txt)\n    .replace(/ELEVENLABS/g, 'MOTOR_VOZ')\n    .replace(/eleven[ _-]?labs/gi, 'MotorVoz')\n    .replace(/11labs/gi, 'MotorVoz')\n    .replace(/agent_[A-Za-z0-9]{8,}/g, 'agente-oculto')\n    .replace(/conv_[A-Za-z0-9]{8,}/g, 'conversa-oculta')\n    .replace(/sk_[A-Za-z0-9]{12,}/g, 'chave-oculta')\n}\nfunction pacoteTexto(ev) {\n  const cru = (ev && ev.cru) ? ev.cru : ev\n  let txt\n  try { txt = JSON.stringify(cru, null, 2) } catch (e) { txt = String(cru) }\n  return limpaMotor(txt)\n}\nfunction sipTexto(ev) {\n  const det = (ev && ev.cru && ev.cru.eventDetails) || {}\n  const msg = det.sipMessage || (ev && ev.sip) || ''\n  return msg ? limpaMotor(String(msg).replace(/\\r\\n/g, '\\n')) : ''\n}\n\n// Refer-To do Genesys: sip:<numero>@<host>[:porta][;user=phone][?User-to-User=<hex>]\n// Ordem obrigatoria (RFC 3261 19.1.1): parametros de URI com ';' vem ANTES dos\n// headers embutidos com '?'. Inverter isso faz o SBC ler o user=phone como parte\n// do valor do header.\n// O campo livre passa por cima de tudo: quando o parceiro dita a sintaxe exata,\n// e ela que vai para o fio, sem a gente remontar.\nfunction montaReferTo(d2, hostPadrao) {\n  const livre = (d2.state.referLivre || '').trim()\n  if (livre) return livre.replace(/^</, '').replace(/>$/, '')\n  const host = (d2.state.hostTexto || '').trim() || hostPadrao\n  let uri = 'sip:' + (d2.state.ddrTexto || '') + '@' + host\n  if (d2.state.userPhone) uri += ';user=phone'\n  const u2u = (d2.state.u2uTexto || '').trim()\n  if (u2u) {\n    let valor = /encoding=/i.test(u2u) ? u2u : u2u + ';encoding=hex'\n    // ';' e '=' sao separadores reservados dentro do header embutido na URI:\n    // parser estrito recusa a forma crua. Escapar deixa igual ao que funciona.\n    if (d2.state.escaparU2u) valor = valor.replace(/;/g, '%3B').replace(/=/g, '%3D')\n    uri += '?User-to-User=' + valor\n  }\n  return uri\n}\n\n// Mostra o que o hex do U2U carrega, para conferir a olho antes de disparar.\nfunction decodeHex(hex) {\n  const limpo = String(hex).split(';')[0].replace(/[^0-9a-fA-F]/g, '')\n  if (limpo.length < 2 || limpo.length % 2 !== 0) return '(hex incompleto)'\n  let saida = ''\n  for (let i = 0; i < limpo.length; i += 2) {\n    const c = parseInt(limpo.slice(i, i + 2), 16)\n    saida += (c >= 32 && c < 127) ? String.fromCharCode(c) : '·'\n  }\n  return saida\n}\n\nfunction hostSbcDe(det) {\n  const h = (det && det.headers) || {}\n  const frm = h.from || h.From || ''\n  const m = String(frm).match(/@([0-9A-Za-z.\\-]+)/)\n  return m ? m[1] : 'natura-br.data2.tel'\n}\nfunction hostSbc(d2) { return hostSbcDe(d2.state.det) }"}},"ddrPadrao":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"ddrPadrao"}}}},"rushLive":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"rushLive"}}}},"rushSinal":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"rushSinal"}}}}}}}}}},{"_id":"6a8ec8074587a9dc6536a87b","component":"data2-core-types@Component","system":"65a86528721f5a092783289d","params":{"extends":"data2-core-primitives@Block","component":"kUraMonitor","package":"natura-br","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"export default {\n  css: `\n    :host, .wrap { height: 100%; }\n    * { box-sizing: border-box; }\n    .wrap { display:flex; flex-direction:column; height:100%; background:#0e0f14; color:#e8e8ee;\n            font:13px/1.5 -apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif; }\n    .topo { display:flex; align-items:center; gap:12px; padding:12px 16px; border-bottom:1px solid #23252d;\n            background:#12141b; flex-wrap:wrap }\n    .topo h1 { font-size:14px; font-weight:600; margin:0; letter-spacing:.02em }\n    .topo .sub { color:#6a6a76; font-size:11.5px }\n    .cresce { flex:1 }\n    input, select { background:#151720; border:1px solid #2a2c35; color:#e8e8ee; border-radius:6px;\n                    padding:6px 9px; font:inherit; font-size:12.5px }\n    input::placeholder { color:#565663 }\n    button { background:#1b1d27; border:1px solid #2f3240; color:#cfcfe0; border-radius:6px;\n             padding:6px 11px; font:inherit; font-size:12.5px; cursor:pointer }\n    button:hover { background:#242736 }\n    button.pri { background:#2b6cff22; border-color:#2b6cff66; color:#9ec8f0 }\n    .corpo { flex:1; display:flex; min-height:0 }\n    .lista { width:44%; min-width:340px; border-right:1px solid #23252d; overflow:auto }\n    .det { flex:1; overflow:auto; padding:16px 18px }\n    .filtros { display:flex; gap:8px; padding:10px 12px; border-bottom:1px solid #1c1e26; flex-wrap:wrap;\n               position:sticky; top:0; background:#0e0f14; z-index:2 }\n    .filtros input { width:120px } .filtros input.busca { width:150px }\n    table { width:100%; border-collapse:collapse }\n    tr.linha { cursor:pointer; border-bottom:1px solid #191b22 }\n    tr.linha:hover td { background:#161822 }\n    tr.linha.sel td { background:#1a2030 }\n    td { padding:9px 11px; vertical-align:top }\n    .quando { color:#9a9aa8; font-size:11px; white-space:nowrap }\n    .cid { font-family:ui-monospace,Menlo,monospace; font-size:11px; color:#7f8b9e }\n    .chip { display:inline-block; padding:1px 8px; border-radius:999px; font-size:10.5px;\n            border:1px solid #2f3240; color:#b7b7c6; white-space:nowrap }\n    .chip.ok { background:#1c3326; border-color:#2f6f47; color:#8fe0ab }\n    .chip.warn { background:#3a2f14; border-color:#8a6a1f; color:#e6c76a }\n    .chip.bad { background:#3a1c1c; border-color:#8a3030; color:#f0a0a0 }\n    .chip.info { background:#16283a; border-color:#2f5f8f; color:#9ec8f0 }\n    .metricas { display:grid; grid-template-columns:repeat(auto-fill,minmax(120px,1fr)); gap:10px; margin:6px 0 18px }\n    .kpi { background:#12141b; border:1px solid #23252d; border-radius:8px; padding:10px 12px }\n    .kpi .v { font-size:19px; font-weight:600 }\n    .kpi .l { font-size:10.5px; color:#6a6a76; text-transform:uppercase; letter-spacing:.06em; margin-top:2px }\n    h2 { font-size:12px; text-transform:uppercase; letter-spacing:.08em; color:#7a7a86; margin:18px 0 8px;\n         font-weight:600 }\n    .conversa { display:flex; flex-direction:column; gap:7px }\n    .bolha { max-width:82%; padding:8px 11px; border-radius:10px; font-size:12.5px; white-space:pre-wrap;\n             word-break:break-word }\n    .bolha.user { align-self:flex-end; background:#243044; border:1px solid #2f4361; border-bottom-right-radius:3px }\n    .bolha.agent { align-self:flex-start; background:#171a22; border:1px solid #262a35; border-bottom-left-radius:3px }\n    .bolha .quem { font-size:9.5px; text-transform:uppercase; letter-spacing:.08em; color:#6a6a76; margin-bottom:3px }\n    .tool { align-self:center; font-size:10.5px; color:#8a7fd0; background:#1a172a; border:1px solid #332a55;\n            padding:3px 10px; border-radius:999px; font-family:ui-monospace,Menlo,monospace }\n    .svc { display:flex; align-items:center; gap:10px; padding:7px 10px; border:1px solid #23252d;\n           border-radius:7px; margin-bottom:6px; background:#12141b }\n    .svc .ord { width:22px; height:22px; border-radius:50%; background:#1b1d27; display:flex; align-items:center;\n                justify-content:center; font-size:11px; color:#8a8a98; flex-shrink:0 }\n    .svc .uc { font-weight:600; font-size:12px }\n    .svc .lat { margin-left:auto; color:#7a7a86; font-size:11px }\n    .grav { border:1px dashed #2f3240; border-radius:8px; padding:14px; background:#12141b; color:#9a9aa8;\n            font-size:12px; line-height:1.6 }\n    .grav audio { width:100%; margin-top:8px }\n    .vazio { color:#565663; padding:40px 16px; text-align:center; font-size:12.5px }\n    .portao { display:flex; align-items:center; justify-content:center; height:100%; }\n    .caixa { width:320px; background:#12141b; border:1px solid #23252d; border-radius:12px; padding:26px }\n    .caixa h1 { font-size:15px; margin:0 0 4px } .caixa .sub { color:#6a6a76; font-size:12px; margin-bottom:16px }\n    .caixa input { width:100% } .caixa button { width:100%; margin-top:10px }\n    .hint { color:#6a6a76; font-size:11px }\n  `,\n\n  mounted(d2) {\n    d2.setState({\n      chamadas: [], sel: null, det: null, carregando: false, carregandoDet: false,\n      total: 0, erro: null, negado: false,\n      busca: '', outcome: '', consultant: '', desde: '',\n      senha: (function () { try { return localStorage.getItem('ura-monitor-senha') || '' } catch (e) { return '' } })()\n    })\n\n    const refMon = d2.param('rushMonitor', 'poc-ura-monitor')\n    const senhaFixa = d2.param('senhaParam', '')\n    const senhaDe = () => senhaFixa || d2.state.senha || ''\n    const comSenha = (o) => Object.assign({ senha: senhaDe() }, o || {})\n\n    const puxarLista = async () => {\n      if (!senhaDe()) return\n      d2.setState({ carregando: true, erro: null })\n      try {\n        const corpo = comSenha({ modo: 'lista', limite: 200 })\n        if ((d2.state.busca || '').trim()) corpo.consultant_id = undefined\n        if ((d2.state.consultant || '').trim()) corpo.consultant_id = d2.state.consultant.trim()\n        if ((d2.state.outcome || '').trim()) corpo.outcome = d2.state.outcome.trim()\n        if ((d2.state.desde || '').trim()) corpo.desde = new Date(d2.state.desde).toISOString()\n        const r = await d2.rush(refMon).invoke(corpo)\n        const out = (r && r.data) ? r.data : r\n        if (out && out.erro === 'acesso negado') { d2.setState({ negado: true, carregando: false }); return }\n        let linhas = out.chamadas || []\n        const q = (d2.state.busca || '').trim().toLowerCase()\n        if (q) linhas = linhas.filter(c =>\n          String(c.call_id || '').toLowerCase().includes(q) ||\n          String(c.consultant_id || '').toLowerCase().includes(q) ||\n          String(c.caller_number || '').toLowerCase().includes(q))\n        d2.setState({ chamadas: linhas, total: out.total || linhas.length, carregando: false, negado: false })\n      } catch (e) {\n        d2.setState({ erro: String((e && e.message) || e), carregando: false })\n      }\n    }\n\n    const puxarDetalhe = async (callId) => {\n      d2.setState({ carregandoDet: true, det: null })\n      try {\n        const r = await d2.rush(refMon).invoke(comSenha({ modo: 'detalhe', call_id: callId }))\n        const out = (r && r.data) ? r.data : r\n        d2.setState({ det: (out && out.detalhe) || null, carregandoDet: false })\n      } catch (e) {\n        d2.setState({ det: null, carregandoDet: false, erro: String((e && e.message) || e) })\n      }\n    }\n\n    d2.on(d2.root, 'click', async (e) => {\n      const alvo = e.target\n      if (!alvo || !alvo.closest) return\n\n      const entrar = alvo.closest('[data-entrar]')\n      if (entrar) {\n        const campo = d2.$('#senha'); const v = (campo && campo.value || '').trim()\n        try { localStorage.setItem('ura-monitor-senha', v) } catch (er) {}\n        d2.setState({ senha: v, negado: false }); puxarLista(); return\n      }\n      const atualizar = alvo.closest('[data-atualizar]')\n      if (atualizar) { puxarLista(); return }\n      const linha = alvo.closest('[data-call]')\n      if (linha) { d2.setState({ sel: linha.dataset.call }); puxarDetalhe(linha.dataset.call); return }\n    })\n\n    d2.on(d2.root, 'input', (e) => {\n      const el = e.target\n      if (!el || !el.dataset || !el.dataset.campo) return\n      const p = {}; p[el.dataset.campo] = el.value; d2.setState(p)\n    })\n    d2.on(d2.root, 'change', (e) => {\n      const el = e.target\n      if (el && el.dataset && el.dataset.campo === 'outcome') { d2.setState({ outcome: el.value }); puxarLista() }\n    })\n    d2.on(d2.root, 'keydown', (e) => {\n      if (e.key === 'Enter' && e.target && e.target.dataset && e.target.dataset.campo) puxarLista()\n    })\n\n    if (senhaDe()) puxarLista()\n  },\n\n  render(d2) {\n    const esc = (s) => String(s == null ? '' : s).replace(/[&<>\"]/g, c => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;' }[c]))\n\n    if (d2.state.negado || (!d2.param('senhaParam', '') && !d2.state.senha)) {\n      return `<div class=\"portao\"><div class=\"caixa\">\n        <h1>Monitoramento da URA</h1>\n        <div class=\"sub\">acesso restrito · time de operação</div>\n        <input type=\"password\" id=\"senha\" placeholder=\"senha de acesso\" autocomplete=\"current-password\">\n        <button class=\"pri\" data-entrar=\"1\">entrar</button>\n        ${d2.state.negado ? '<div class=\"hint\" style=\"margin-top:10px;color:#f0a0a0\">senha incorreta</div>' : ''}\n      </div></div>`\n    }\n\n    const outChip = (o) => {\n      const m = {\n        retido: 'ok', resolvido: 'ok', sucesso: 'ok', concluido: 'ok',\n        transferido: 'info', transferencia: 'info',\n        abandonado: 'bad', erro: 'bad', falha: 'bad', caiu: 'bad'\n      }\n      const k = m[String(o || '').toLowerCase()] || 'warn'\n      return `<span class=\"chip ${k}\">${esc(o || '—')}</span>`\n    }\n    const dur = (s) => { s = Number(s) || 0; const m = Math.floor(s / 60), r = Math.round(s % 60); return m ? `${m}m${String(r).padStart(2, '0')}s` : `${r}s` }\n    const hhmm = (t) => { try { return new Date(t).toLocaleString('pt-BR', { day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit' }) } catch (e) { return esc(t) } }\n\n    const linhas = d2.state.chamadas || []\n    const listaHtml = linhas.length ? `<table>${linhas.map(c => `\n      <tr class=\"linha ${d2.state.sel === c.call_id ? 'sel' : ''}\" data-call=\"${esc(c.call_id)}\">\n        <td>\n          <div class=\"quando\">${hhmm(c.timestamp)}</div>\n          <div class=\"cid\">${esc(String(c.call_id || '').slice(0, 20))}</div>\n        </td>\n        <td>\n          <div>${esc(c.consultant_id || 'sem id')}</div>\n          <div class=\"hint\">${esc(c.caller_number || '')}</div>\n        </td>\n        <td>${outChip(c.outcome)}\n          ${c.transferred_done ? '<span class=\"chip info\" style=\"margin-left:4px\">transf ✓</span>'\n            : (c.transferred_intent ? '<span class=\"chip warn\" style=\"margin-left:4px\">transf?</span>' : '')}\n        </td>\n        <td style=\"text-align:right;white-space:nowrap\">\n          <div>${dur(c.duration_s)}</div>\n          ${c.csat_score != null ? `<div class=\"hint\">CSAT ${esc(c.csat_score)}</div>` : ''}\n        </td>\n      </tr>`).join('')}</table>`\n      : `<div class=\"vazio\">${d2.state.carregando ? 'carregando…' : (d2.state.erro ? 'erro: ' + esc(d2.state.erro) : 'nenhuma chamada')}</div>`\n\n    return `<div class=\"wrap\">\n      <div class=\"topo\">\n        <h1>Monitoramento da URA</h1>\n        <span class=\"sub\">${d2.state.total || 0} chamadas${d2.state.carregando ? ' · carregando…' : ''}</span>\n        <span class=\"cresce\"></span>\n        <button data-atualizar=\"1\">↻ atualizar</button>\n      </div>\n      <div class=\"corpo\">\n        <div class=\"lista\">\n          <div class=\"filtros\">\n            <input class=\"busca\" data-campo=\"busca\" value=\"${esc(d2.state.busca)}\" placeholder=\"🔎 call_id / consultora / número\">\n            <select data-campo=\"outcome\">\n              <option value=\"\">todos desfechos</option>\n              ${['retido', 'transferido', 'abandonado', 'resolvido', 'erro'].map(o =>\n                `<option value=\"${o}\" ${d2.state.outcome === o ? 'selected' : ''}>${o}</option>`).join('')}\n            </select>\n            <input type=\"date\" data-campo=\"desde\" value=\"${esc(d2.state.desde)}\" title=\"a partir de\">\n            <button data-atualizar=\"1\">filtrar</button>\n          </div>\n          ${listaHtml}\n        </div>\n        <div class=\"det\">${this.renderDet(d2, esc, dur, hhmm, outChip)}</div>\n      </div>\n    </div>`\n  },\n\n  renderDet(d2, esc, dur, hhmm, outChip) {\n    if (d2.state.carregandoDet) return `<div class=\"vazio\">carregando detalhe…</div>`\n    const det = d2.state.det\n    if (!det) return `<div class=\"vazio\">selecione uma chamada à esquerda para ver a transcrição, os serviços consumidos e as métricas.</div>`\n    const c = det.chamada || {}\n    const tr = Array.isArray(det.transcript) ? det.transcript : []\n    const svc = Array.isArray(det.servicos) ? det.servicos : []\n\n    const kpi = (v, l) => `<div class=\"kpi\"><div class=\"v\">${v == null || v === '' ? '—' : esc(v)}</div><div class=\"l\">${esc(l)}</div></div>`\n\n    const acaoDe = (turno) => {\n      try {\n        if (turno.tool_calls && turno.tool_calls[0]) {\n          const raw = turno.tool_calls[0].params_as_json || (turno.tool_calls[0].tool_details && turno.tool_calls[0].tool_details.body)\n          const p = typeof raw === 'string' ? JSON.parse(raw) : raw\n          return (p && (p.action || p.name)) || null\n        }\n      } catch (e) {}\n      return null\n    }\n\n    const conversa = tr.length ? tr.map(t => {\n      const role = t.role || (t.source) || ''\n      const msg = t.message || t.text || ''\n      const acao = acaoDe(t)\n      if (acao && !msg) return `<div class=\"tool\">⚙ ${esc(acao)}</div>`\n      if (!msg && !acao) return ''\n      const ehUser = /user|caller|cliente|consultora/i.test(role)\n      return `<div class=\"bolha ${ehUser ? 'user' : 'agent'}\">\n        <div class=\"quem\">${ehUser ? 'consultora' : 'assistente'}${acao ? ' · ' + esc(acao) : ''}</div>\n        ${esc(msg)}</div>`\n    }).join('') : `<div class=\"hint\">sem transcrição registrada nesta chamada.</div>`\n\n    const servicos = svc.length ? svc.map((s, i) => `\n      <div class=\"svc\">\n        <div class=\"ord\">${esc(s.ordem != null ? s.ordem + 1 : i + 1)}</div>\n        <div><div class=\"uc\">${esc(s.usecase || '—')}</div>\n          ${s.parametros ? `<div class=\"hint\" style=\"font-family:ui-monospace,Menlo,monospace\">${esc(String(s.parametros).slice(0, 120))}</div>` : ''}</div>\n        ${s.erro ? '<span class=\"chip bad\">erro</span>' : ''}\n        <div class=\"lat\">${s.latencia_s != null ? esc(s.latencia_s) + 's' : ''}</div>\n      </div>`).join('') : `<div class=\"hint\">nenhum serviço consumido.</div>`\n\n    const audioUrl = c.audio_url || det.audio_url || null\n    const gravacao = audioUrl\n      ? `<div class=\"grav\">Gravação da ligação<audio controls preload=\"none\" src=\"${esc(audioUrl)}\"></audio></div>`\n      : `<div class=\"grav\">\n          <b style=\"color:#c9c9d6\">Gravação</b> — conversa <span class=\"cid\">${esc(c.conversation_id || '—')}</span><br>\n          A reprodução do áudio requer habilitar a retenção no motor de voz e um proxy de mídia (como no atendimento Pay).\n          Está preparado no monitor: assim que houver <span class=\"cid\">audio_url</span> na chamada, o player aparece aqui.\n        </div>`\n\n    return `\n      <div style=\"display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-bottom:4px\">\n        ${outChip(c.outcome)}\n        <span class=\"cid\">${esc(c.call_id || '')}</span>\n        <span class=\"hint\">${hhmm(c.timestamp)} · ${esc(c.consultant_id || 'sem id')} · ${esc(c.caller_number || '')}</span>\n        ${c.env ? `<span class=\"chip\">${esc(c.env)}</span>` : ''}\n      </div>\n      <div class=\"metricas\">\n        ${kpi(dur(c.duration_s), 'duração')}\n        ${kpi(c.csat_score, 'CSAT')}\n        ${kpi(c.retention, 'retenção')}\n        ${kpi((c.user_turns || 0) + '/' + (c.agent_turns || 0), 'turnos u/a')}\n        ${kpi(c.usecases_count, 'casos de uso')}\n        ${kpi(c.tool_calls, 'tool calls')}\n        ${kpi(c.tool_errors, 'erros')}\n        ${kpi(c.ai_tokens, 'tokens IA')}\n        ${kpi(c.transferred_done ? 'sim' : (c.transferred_intent ? 'tentou' : 'não'), 'transferência')}\n      </div>\n      <h2>Transcrição</h2>\n      <div class=\"conversa\">${conversa}</div>\n      <h2>Serviços consumidos</h2>\n      ${servicos}\n      <h2>Gravação</h2>\n      ${gravacao}`\n  }\n}"}},"rushMonitor":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"rushMonitor"}}}},"senhaParam":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"senhaParam"}}}}}}}}}},{"_id":"6a8ed018383002de23ef5f3a","component":"data2-core-types@Component","system":"65a86528721f5a092783289d","params":{"extends":"data2-core-primitives@Block","component":"kUraBiz","package":"natura-br","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"export default {\n  css: `\n    :host,.wrap{height:100%}\n    *{box-sizing:border-box}\n    .wrap{height:100%;overflow:auto;background:#f5f6f8;color:#1a1d24;\n      font:13px/1.5 -apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif}\n    .top{display:flex;align-items:center;gap:14px;padding:18px 26px;background:#fff;border-bottom:1px solid #e8eaee;\n      position:sticky;top:0;z-index:5;flex-wrap:wrap}\n    .top h1{font-size:17px;font-weight:650;margin:0;letter-spacing:-.01em}\n    .top .sub{color:#8a909b;font-size:12px}\n    .cresce{flex:1}\n    .seg{display:inline-flex;border:1px solid #e2e5ea;border-radius:8px;overflow:hidden;background:#fff}\n    .seg button{border:0;background:#fff;color:#5b616e;padding:6px 12px;font:inherit;font-size:12px;cursor:pointer}\n    .seg button.on{background:#2b6cff;color:#fff}\n    input{border:1px solid #e2e5ea;border-radius:8px;padding:7px 10px;font:inherit;font-size:12.5px;background:#fff;color:#1a1d24}\n    button.gh{border:1px solid #e2e5ea;background:#fff;color:#3b4150;border-radius:8px;padding:7px 12px;font:inherit;\n      font-size:12.5px;cursor:pointer}\n    button.gh:hover{background:#f0f2f5}\n    .body{padding:22px 26px;display:flex;flex-direction:column;gap:20px;max-width:1500px;margin:0 auto}\n    .kpis{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:14px}\n    .card{background:#fff;border:1px solid #e8eaee;border-radius:14px;padding:16px 18px;box-shadow:0 1px 2px rgba(20,25,40,.03)}\n    .kpi .v{font-size:26px;font-weight:680;letter-spacing:-.02em}\n    .kpi .l{font-size:11.5px;color:#8a909b;margin-top:3px}\n    .kpi .d{font-size:11px;margin-top:6px}\n    .up{color:#16a34a}.down{color:#dc2626}\n    .grid2{display:grid;grid-template-columns:2fr 1fr;gap:18px}\n    .grid2b{display:grid;grid-template-columns:1fr 1fr;gap:18px}\n    @media(max-width:1000px){.grid2,.grid2b{grid-template-columns:1fr}}\n    h2{font-size:13px;font-weight:640;margin:0 0 14px;color:#3b4150}\n    .bar-row{display:flex;align-items:center;gap:10px;margin-bottom:9px;font-size:12px}\n    .bar-row .lab{width:150px;color:#4b515e;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex-shrink:0}\n    .bar-row .track{flex:1;height:16px;background:#f0f2f5;border-radius:5px;overflow:hidden}\n    .bar-row .fill{height:100%;background:#2b6cff;border-radius:5px}\n    .bar-row .val{width:52px;text-align:right;color:#6b7280;font-variant-numeric:tabular-nums;flex-shrink:0}\n    .colbars{display:flex;align-items:flex-end;gap:5px;height:150px;padding-top:8px}\n    .colbars .col{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:flex-end;gap:5px;min-width:0}\n    .colbars .cb{width:70%;max-width:26px;background:#2b6cff;border-radius:4px 4px 0 0;min-height:2px;transition:height .2s}\n    .colbars .cd{font-size:9.5px;color:#9aa0ab;white-space:nowrap;transform:rotate(-45deg);transform-origin:top;height:26px}\n    .tbl{width:100%;border-collapse:collapse;font-size:12.5px}\n    .tbl th{text-align:left;color:#8a909b;font-weight:550;font-size:11px;text-transform:uppercase;letter-spacing:.04em;\n      padding:8px 10px;border-bottom:1px solid #eceef2}\n    .tbl td{padding:10px;border-bottom:1px solid #f1f3f6;vertical-align:middle}\n    .tbl tr.click{cursor:pointer}\n    .tbl tr.click:hover td{background:#f7f9fc}\n    .pill{display:inline-block;padding:2px 9px;border-radius:999px;font-size:11px;font-weight:550}\n    .pill.g{background:#e7f6ec;color:#16833c}.pill.b{background:#fdeaea;color:#c62b2b}\n    .pill.a{background:#fdf3e3;color:#b3730c}.pill.i{background:#e9f0fe;color:#2357c7}.pill.n{background:#eef0f3;color:#6b7280}\n    .mono{font-family:ui-monospace,Menlo,monospace;font-size:11px;color:#8a909b}\n    .drawer{position:fixed;top:0;right:0;bottom:0;width:min(560px,94vw);background:#fff;border-left:1px solid #e2e5ea;\n      box-shadow:-8px 0 30px rgba(20,25,40,.12);z-index:20;display:flex;flex-direction:column}\n    .drawer .dh{padding:16px 20px;border-bottom:1px solid #eceef2;display:flex;align-items:center;gap:10px}\n    .drawer .dc{flex:1;overflow:auto;padding:18px 20px}\n    .x{margin-left:auto;cursor:pointer;color:#8a909b;font-size:20px;line-height:1;border:0;background:0}\n    .metg{display:grid;grid-template-columns:repeat(auto-fill,minmax(96px,1fr));gap:10px;margin-bottom:16px}\n    .metg .m{background:#f7f9fc;border:1px solid #eceef2;border-radius:9px;padding:9px 11px}\n    .metg .m .mv{font-size:16px;font-weight:640}.metg .m .ml{font-size:10px;color:#8a909b;margin-top:1px}\n    .conv{display:flex;flex-direction:column;gap:7px;margin-bottom:8px}\n    .bub{max-width:85%;padding:8px 11px;border-radius:11px;font-size:12.5px;white-space:pre-wrap;word-break:break-word}\n    .bub.u{align-self:flex-end;background:#eaf1ff;border:1px solid #d6e2ff}\n    .bub.a{align-self:flex-start;background:#f4f5f7;border:1px solid #e8eaee}\n    .bub .q{font-size:9.5px;text-transform:uppercase;letter-spacing:.06em;color:#9aa0ab;margin-bottom:3px}\n    .tl{align-self:center;font-size:10.5px;color:#7c5fd0;background:#f2effc;border:1px solid #e2d9fa;padding:2px 9px;border-radius:999px}\n    .svc{display:flex;align-items:center;gap:9px;padding:7px 10px;border:1px solid #eceef2;border-radius:8px;margin-bottom:6px}\n    .svc .o{width:20px;height:20px;border-radius:50%;background:#eef0f3;display:flex;align-items:center;justify-content:center;\n      font-size:10px;color:#6b7280;flex-shrink:0}\n    .svc .lat{margin-left:auto;color:#9aa0ab;font-size:11px}\n    .grav{border:1px solid #e8eaee;border-radius:10px;padding:14px;background:#f7f9fc}\n    .grav audio{width:100%;margin-top:10px}\n    .btn{background:#2b6cff;color:#fff;border:0;border-radius:8px;padding:8px 14px;font:inherit;font-size:12.5px;cursor:pointer}\n    .btn:disabled{opacity:.5;cursor:default}\n    .h2sub{font-weight:400;color:#9aa0ab;font-size:11px}\n    .filtros{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:11px 26px;background:#fff;border-bottom:1px solid #e8eaee}\n    .filtros select,.filtros input[type=date]{border:1px solid #e2e5ea;border-radius:8px;padding:6px 9px;font:inherit;\n      font-size:12px;background:#fff;color:#3b4150;max-width:220px}\n    .filtros select.on,.filtros input[type=date].on{border-color:#2b6cff;color:#2357c7;background:#f5f8ff}\n    .chk{display:inline-flex;align-items:center;gap:6px;font-size:12px;color:#4b515e;cursor:pointer}\n    .mot{font-size:12px;color:#3b4150}\n    .mot .fr{color:#9aa0ab;font-size:10.5px;display:block;max-width:230px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n    .vazio{color:#9aa0ab;padding:30px;text-align:center}\n    .portao{display:flex;align-items:center;justify-content:center;height:100%}\n    .caixa{width:340px;background:#fff;border:1px solid #e8eaee;border-radius:16px;padding:30px;box-shadow:0 8px 30px rgba(20,25,40,.08)}\n    .caixa h1{font-size:17px;margin:0 0 5px}.caixa .sub{color:#8a909b;font-size:12.5px;margin-bottom:18px}\n    .caixa input{width:100%}.caixa .btn{width:100%;margin-top:12px}\n    .err{color:#c62b2b;font-size:12px;margin-top:10px}\n    .svc2{border:1px solid #eceef2;border-radius:9px;padding:9px 11px;margin-bottom:7px;background:#fff}\n    .svc2h{display:flex;align-items:center;gap:8px;font-size:12.5px}\n    .svc2h .o{width:20px;height:20px;border-radius:50%;background:#eef0f3;display:flex;align-items:center;\n      justify-content:center;font-size:10px;color:#6b7280;flex-shrink:0}\n    .svc2h .lat{margin-left:auto;color:#9aa0ab;font-size:11px;flex-shrink:0}\n    .kv{display:flex;gap:8px;margin-top:5px;font-size:12px;padding-left:28px}\n    .kv .k{color:#9aa0ab;min-width:46px;font-size:10px;text-transform:uppercase;letter-spacing:.05em;padding-top:2px;flex-shrink:0}\n    .kv .v{color:#3b4150;word-break:break-word}\n    .kv .v.faq{background:#f2effc;border:1px solid #e2d9fa;color:#5b3fa8;border-radius:6px;padding:1px 7px;font-weight:550}\n  `,\n\n  mounted(d2) {\n    d2.setState({\n      fase: 'gate', erro: null,\n      chamadas: [], servicos: [], carregando: false,\n      periodo: 30, busca: '', desde: null,\n      fDesfecho: '', fTransb: '', fMotivo: '', fServico: '', fCsat: '', fErro: false, de: '', ate: '',\n      sel: null, det: null, carregandoDet: false,\n      audioUrl: null, audioMsg: null, buscandoAudio: false,\n      senha: (function(){try{return localStorage.getItem('ura-biz-senha')||''}catch(e){return''}})()\n    })\n    const refBiz = d2.param('rushBiz', 'poc-ura-biz')\n    const refDet = d2.param('rushDetalhe', 'poc-ura-monitor')\n    const refAudio = d2.param('rushAudio', 'poc-ura-audio')\n    const senhaFixa = d2.param('senhaParam', '')\n    const senhaDe = () => senhaFixa || d2.state.senha || ''\n    const com = (o) => Object.assign({ senha: senhaDe() }, o || {})\n\n    const carregar = async () => {\n      if (!senhaDe()) return\n      d2.setState({ carregando: true, erro: null })\n      try {\n        const r = await d2.rush(refBiz).invoke(com({}))\n        const out = (r && r.data) ? r.data : r\n        if (out && out.erro === 'acesso negado') { d2.setState({ fase: 'gate', erro: 'senha incorreta', carregando: false }); return }\n        d2.setState({ fase: 'painel', chamadas: out.chamadas || [], servicos: out.servicos || [], desde: out.desde || null, carregando: false })\n      } catch (e) { d2.setState({ erro: String((e && e.message) || e), carregando: false }) }\n    }\n\n    const abrir = async (callId, cid) => {\n      d2.setState({ sel: callId, det: null, carregandoDet: true, audioUrl: null, audioMsg: null })\n      try {\n        const r = await d2.rush(refDet).invoke(com({ modo: 'detalhe', call_id: callId }))\n        const out = (r && r.data) ? r.data : r\n        d2.setState({ det: (out && out.detalhe) || null, carregandoDet: false })\n      } catch (e) { d2.setState({ carregandoDet: false, det: null }) }\n    }\n\n    // o rush faz tudo server-side: ElevenLabs -> Drive -> URL (com cache em audio_url)\n    const ouvir = async () => {\n      const callId = d2.state.sel\n      d2.log('[audio] 0) clique ouvir, sel =', callId)\n      if (!callId) { d2.setState({ audioMsg: 'nenhuma chamada selecionada' }); return }\n      d2.setState({ buscandoAudio: true, audioMsg: null, audioUrl: null })\n      let out\n      try {\n        const r = await d2.rush(refAudio).invoke(com({ call_id: callId }))\n        out = (r && r.data) ? r.data : r\n      } catch (e) {\n        d2.log('[audio] X) rush falhou', e)\n        d2.setState({ audioMsg: 'falha ao buscar a gravação: ' + ((e && e.message) || e), buscandoAudio: false })\n        return\n      }\n      d2.log('[audio] 1) rush devolveu', out)\n      if (!out || !out.ok) { d2.setState({ audioMsg: (out && out.motivo) || 'gravação indisponível', buscandoAudio: false }); return }\n      if (!out.url) { d2.setState({ audioMsg: 'o motor de voz não devolveu áudio para esta chamada', buscandoAudio: false }); return }\n      d2.log('[audio] 2) url =', out.url, '· cacheado =', out.cacheado)\n      d2.setState({ audioUrl: out.url, buscandoAudio: false })\n    }\n\n    d2.on(d2.root, 'click', (e) => {\n      const a = e.target; if (!a || !a.closest) return\n      const ent = a.closest('[data-entrar]')\n      if (ent) { const c = d2.$('#senha'); const v = (c && c.value || '').trim()\n        try { localStorage.setItem('ura-biz-senha', v) } catch (er) {}\n        d2.setState({ senha: v }); carregar(); return }\n      const rf = a.closest('[data-refresh]'); if (rf) { carregar(); return }\n      const pr = a.closest('[data-periodo]'); if (pr) { d2.setState({ periodo: Number(pr.dataset.periodo) }); return }\n      const ln = a.closest('[data-call]'); if (ln) { abrir(ln.dataset.call, ln.dataset.cid); return }\n      const fc = a.closest('[data-fechar]'); if (fc) { d2.setState({ sel: null, det: null }); return }\n      const au = a.closest('[data-ouvir]'); if (au) { ouvir(); return }\n      const lm = a.closest('[data-limpar]')\n      if (lm) { d2.setState({ fDesfecho:'', fTransb:'', fMotivo:'', fServico:'', fCsat:'', fErro:false, de:'', ate:'', busca:'' }); return }\n    })\n    d2.on(d2.root, 'change', (e) => {\n      const el = e.target; if (!el || !el.dataset || !el.dataset.filtro) return\n      d2.setState({ [el.dataset.filtro]: el.type === 'checkbox' ? el.checked : el.value })\n    })\n    d2.on(d2.root, 'input', (e) => { const el = e.target; if (el && el.dataset && el.dataset.campo === 'busca') d2.setState({ busca: el.value }) })\n\n    if (senhaDe()) { d2.setState({ fase: 'painel' }); carregar() }\n  },\n\n  render(d2) {\n    const esc = (s) => String(s == null ? '' : s).replace(/[&<>\"]/g, c => ({ '&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;' }[c]))\n    if (d2.state.fase === 'gate') {\n      return `<div class=\"portao\"><div class=\"caixa\">\n        <h1>Painel da URA de Voz</h1>\n        <div class=\"sub\">indicadores de atendimento · acesso restrito</div>\n        <input type=\"password\" id=\"senha\" placeholder=\"senha de acesso\" autocomplete=\"current-password\">\n        <button class=\"btn\" data-entrar=\"1\">entrar</button>\n        ${d2.state.erro ? `<div class=\"err\">${esc(d2.state.erro)}</div>` : ''}\n      </div></div>`\n    }\n\n    const dur = (s) => { s = Math.round(Number(s)||0); const m = Math.floor(s/60), r = s%60; return m ? `${m}m${String(r).padStart(2,'0')}` : `${r}s` }\n    const agora = Date.now()\n    const piso = d2.state.desde ? new Date(d2.state.desde).getTime() : -Infinity\n    const janela = d2.state.periodo > 0 ? d2.state.periodo * 86400000 : Infinity\n    const dentro = (t) => { const x = new Date(t).getTime(); return x >= piso && (isFinite(janela) ? (agora - x) <= janela : true) }\n    const q = (d2.state.busca||'').trim().toLowerCase()\n\n    // ── nomes de negócio para os use cases\n    const NOMES = {\n      CONSULT_FAQ:'Dúvida (FAQ)', CONSULT_CYCLE_AND_CREDITS:'Ciclo e créditos',\n      SEARCH_LIST_ORDERS:'Meus pedidos', SEARCH_LIST_DIGITAL_SPACE_ORDERS:'Pedidos do espaço digital',\n      USER_CONSULTANT_INFORMATION:'Meus dados', DETALHES_ITEMS_PEDIDO_BY_ID:'Itens de um pedido',\n      WHO_IS_MY_LEADER_AND_MANAGER:'Minha liderança', FALAR_COM_ATENDIMENTO:'Pedido de atendente',\n      LISTAR_PAGAMENTOS_EM_ABERTO:'Pagamentos em aberto', LISTAR_PAGAMENTOS_VENCIDOS:'Pagamentos vencidos',\n      LISTAR_PAGAMENTOS_PAGOS_QUITADOS:'Pagamentos quitados',\n      LISTAR_DIVIDAS_E_PAGAMENTOS_ELEGIVEIS_PARA_RENEGOCIACAO:'Renegociação — elegíveis',\n      '1_ESCOLHER_METODO_DE_PAGAMENTO_DA_RENEGOCIACAO':'Renegociação — método',\n      '2_ESCOLHER_OPCAO_RENEGOCIACAO_TOTAL_PARCIAL_PARCELADO':'Renegociação — opção',\n      '3_ESCOLHER_OPCOES_PARCELAMENTO_E_DATA_DE_INICIO_DE_PAGAMENTO':'Renegociação — parcelamento',\n      '4_SIMULAR_RENEGOCIACAO':'Renegociação — simulação', '5_EXECUTAR_RENEGOCIACAO':'Renegociação — fechamento',\n      '6_ENVIAR_RESULTADO_RENEGOCIACAO_POR_EMAIL_OU_WHATSAPP':'Renegociação — envio',\n      '2_SEGUNDA_VIA_BOLETO_LISTAR_BOLETOS':'2ª via — listar boletos', '2_SEGUNDA_VIA_BOLETO_ENVIAR':'2ª via — enviar',\n      BAIXA_PAGAMENTO:'Baixa de pagamento', CONSULT_LEVEL:'Nível', AUMENTO_PONTOS:'Aumento de pontos',\n      RECEBER_AVALIACAO_CSAT:'Avaliação (CSAT)', SEARCH_PRODUCTS:'Busca de produtos', CANCELAR_PEDIDO:'Cancelar pedido'\n    }\n    const rotulo = (u) => NOMES[String(u||'').trim()] ||\n      String(u||'—').replace(/_/g,' ').toLowerCase().replace(/^./, ch => ch.toUpperCase())\n    const ehPedido = (u) => /FALAR_COM_ATENDIMENTO/i.test(String(u||''))\n\n    // ── motivo derivado dos serviços consumidos, por chamada\n    const seqPorCall = {}\n    ;(d2.state.servicos||[]).forEach(s => { (seqPorCall[s.call_id] = seqPorCall[s.call_id] || []).push(s) })\n    Object.keys(seqPorCall).forEach(k => seqPorCall[k].sort((a,b)=>(Number(a.ordem)||0)-(Number(b.ordem)||0)))\n    const infoDe = (c) => {\n      const seq = seqPorCall[c.call_id] || []\n      const iPedido = seq.findIndex(s => ehPedido(s.usecase))\n      const pediu = iPedido >= 0 || !!c.transferred_intent || !!c.transferred_done\n      const antes = (iPedido >= 0 ? seq.slice(0, iPedido) : seq)\n        .filter(s => !ehPedido(s.usecase) && String(s.usecase||'').trim())\n      const ultimo = antes.length ? antes[antes.length-1] : null\n      const motivo = ultimo ? rotulo(ultimo.usecase) : (pediu ? 'Pedido direto' : 'Sem interação')\n      let frase = null\n      if (ultimo && /FAQ/i.test(String(ultimo.usecase||''))) {\n        try { const p = JSON.parse(ultimo.parametros||'{}'); frase = String(p.search_phrase||p.searchPhrase||'').trim()||null } catch (e) {}\n      }\n      return { pediu, motivo, frase, seq }\n    }\n    const info = {}\n    ;(d2.state.chamadas||[]).forEach(c => { info[c.call_id] = infoDe(c) })\n    const inf = (c) => info[c.call_id] || { pediu:false, motivo:'—', frase:null, seq:[] }\n\n    const csatOk = (c) => { const f = d2.state.fCsat; if (!f) return true\n      const v = c.csat_score\n      if (f === 'sem') return v == null\n      if (v == null) return false\n      if (f === 'baixo') return v <= 2\n      if (f === 'neutro') return Number(v) === 3\n      return v >= 4 }\n    const dataOk = (c) => { const de = d2.state.de, ate = d2.state.ate; if (!de && !ate) return true\n      const t = new Date(c.timestamp).getTime()\n      if (de && t < new Date(de + 'T00:00:00').getTime()) return false\n      if (ate && t > new Date(ate + 'T23:59:59').getTime()) return false\n      return true }\n\n    const chamadas = (d2.state.chamadas||[]).filter(c => dentro(c.timestamp)).filter(dataOk).filter(c => !q ||\n      String(c.call_id||'').toLowerCase().includes(q) || String(c.consultant_id||'').toLowerCase().includes(q) || String(c.caller_number||'').toLowerCase().includes(q))\n      .filter(c => !d2.state.fDesfecho || String(c.outcome||'') === d2.state.fDesfecho)\n      .filter(c => !d2.state.fTransb || (d2.state.fTransb === 'sim' ? inf(c).pediu : !inf(c).pediu))\n      .filter(c => !d2.state.fMotivo || inf(c).motivo === d2.state.fMotivo)\n      .filter(c => !d2.state.fServico || inf(c).seq.some(s => String(s.usecase||'') === d2.state.fServico))\n      .filter(c => !d2.state.fErro || Number(c.tool_errors||0) > 0)\n      .filter(csatOk)\n    const callIds = new Set(chamadas.map(c => c.call_id))\n    const servicos = (d2.state.servicos||[]).filter(s => callIds.has(s.call_id))\n\n    const n = chamadas.length\n    const soma = (arr, f) => arr.reduce((a,c)=>a+(Number(f(c))||0),0)\n    const retidas = chamadas.filter(c => /retid/i.test(String(c.outcome||'')) || /total/i.test(String(c.retention||''))).length\n    const transf = chamadas.filter(c => c.transferred_intent || c.transferred_done).length\n    const comCsat = chamadas.filter(c => c.csat_score != null)\n    const csatMed = comCsat.length ? (soma(comCsat, c=>c.csat_score)/comCsat.length) : null\n    const durMed = n ? soma(chamadas, c=>c.duration_s)/n : 0\n    const erros = servicos.filter(s => s.erro).length\n    const consultores = new Set(chamadas.map(c=>c.consultant_id).filter(Boolean)).size\n\n    const kpi = (v,l,extra) => `<div class=\"card kpi\"><div class=\"v\">${v}</div><div class=\"l\">${esc(l)}</div>${extra?`<div class=\"d\">${extra}</div>`:''}</div>`\n\n    // chamadas por dia (últimos min(periodo,21) dias)\n    const diasPiso = isFinite(piso) ? Math.max(1, Math.ceil((agora - piso) / 86400000)) : 21\n    const dias = Math.min(d2.state.periodo>0?d2.state.periodo:diasPiso, diasPiso, 21)\n    const porDia = []\n    for (let i = dias-1; i >= 0; i--) {\n      const d = new Date(agora - i*86400000); const key = d.toISOString().slice(0,10)\n      porDia.push({ key, lab: d.toLocaleDateString('pt-BR',{day:'2-digit',month:'2-digit'}),\n        n: chamadas.filter(c => String(c.timestamp||'').slice(0,10) === key).length })\n    }\n    const maxDia = Math.max(1, ...porDia.map(d=>d.n))\n    const colDia = `<div class=\"colbars\">${porDia.map(d=>`<div class=\"col\" title=\"${d.lab}: ${d.n}\">\n      <div class=\"cb\" style=\"height:${Math.round(d.n/maxDia*120)}px\"></div><div class=\"cd\">${d.lab}</div></div>`).join('')}</div>`\n\n    // barras genéricas\n    const barras = (rows, cor) => { const mx = Math.max(1, ...rows.map(r=>r.v)); return rows.length ? rows.map(r=>`\n      <div class=\"bar-row\"><div class=\"lab\" title=\"${esc(r.k)}\">${esc(r.k)}</div>\n        <div class=\"track\"><div class=\"fill\" style=\"width:${Math.round(r.v/mx*100)}%;${cor?`background:${cor}`:''}\"></div></div>\n        <div class=\"val\">${r.v}</div></div>`).join('') : `<div class=\"vazio\">sem dados</div>` }\n\n    const grp = (arr, f) => { const m = {}; arr.forEach(x=>{ const k=f(x)||'—'; m[k]=(m[k]||0)+1 }); \n      return Object.entries(m).map(([k,v])=>({k,v})).sort((a,b)=>b.v-a.v) }\n\n    const porUsecase = grp(servicos, s=>s.usecase).slice(0,10)\n    const porOutcome = grp(chamadas, c=>c.outcome).slice(0,8)\n    const porConsultor = grp(chamadas.filter(c=>c.consultant_id), c=>c.consultant_id).slice(0,8)\n    const transbordou = chamadas.filter(c => inf(c).pediu)\n    const ficouRetida = chamadas.filter(c => !inf(c).pediu)\n    const porMotivoT = grp(transbordou, c => inf(c).motivo).slice(0,10)\n    const porMotivoR = grp(ficouRetida, c => inf(c).motivo).slice(0,10)\n\n    const uniq = (a) => Array.from(new Set(a.filter(Boolean))).sort()\n    const optDesfecho = uniq((d2.state.chamadas||[]).map(c=>c.outcome))\n    const optServico = uniq((d2.state.servicos||[]).map(s=>s.usecase))\n    const optMotivo = uniq((d2.state.chamadas||[]).map(c=>inf(c).motivo))\n    const combo = (campo, vazio, opts, fmt) => { const v = d2.state[campo]||''\n      return `<select data-filtro=\"${campo}\" class=\"${v?'on':''}\"><option value=\"\">${esc(vazio)}</option>${\n        opts.map(o=>`<option value=\"${esc(o)}\"${v===o?' selected':''}>${esc(fmt?fmt(o):o)}</option>`).join('')}</select>` }\n    const nCsat = { baixo:'CSAT 1 e 2', neutro:'CSAT 3', alto:'CSAT 4 e 5', sem:'sem CSAT' }\n    const ativos = ['fDesfecho','fTransb','fMotivo','fServico','fCsat','de','ate'].filter(k=>d2.state[k]).length + (d2.state.fErro?1:0)\n    const buscasFaq = (() => { const m = {}\n      servicos.filter(s => /FAQ/i.test(String(s.usecase||''))).forEach(s => {\n        let p = null; try { p = JSON.parse(s.parametros || '{}') } catch (e) {}\n        const k = String((p && (p.search_phrase || p.searchPhrase)) || '').trim(); if (!k) return\n        m[k] = (m[k]||0)+1 })\n      return Object.entries(m).map(([k,v])=>({k,v})).sort((a,b)=>b.v-a.v).slice(0,10) })()\n\n    const outPill = (o) => { const k = /retid/i.test(o)?'g':/transfer/i.test(o)?'i':/aband|erro|falha/i.test(o)?'b':/devolv/i.test(o)?'a':'n'\n      return `<span class=\"pill ${k}\">${esc(o||'—')}</span>` }\n\n    const linhas = chamadas.slice(0,120).map(c => `\n      <tr class=\"click\" data-call=\"${esc(c.call_id)}\" data-cid=\"${esc(c.conversation_id||'')}\">\n        <td class=\"mono\">${esc(new Date(c.timestamp).toLocaleString('pt-BR',{day:'2-digit',month:'2-digit',hour:'2-digit',minute:'2-digit'}))}</td>\n        <td>${esc(c.consultant_id||'—')}<div class=\"mono\">${esc(c.caller_number||'')}</div></td>\n        <td>${outPill(c.outcome)}${c.transferred_done?' <span class=\"pill i\">transf✓</span>':(c.transferred_intent?' <span class=\"pill a\">transf?</span>':'')}</td>\n        <td><div class=\"mot\">${esc(inf(c).motivo)}${inf(c).frase?`<span class=\"fr\">“${esc(inf(c).frase)}”</span>`:''}</div></td>\n        <td style=\"text-align:right\">${dur(c.duration_s)}</td>\n        <td style=\"text-align:right\">${c.usecases_count||0}</td>\n        <td style=\"text-align:right\">${c.csat_score!=null?esc(c.csat_score):'—'}</td>\n      </tr>`).join('')\n\n    const seg = (v,l) => `<button class=\"${d2.state.periodo===v?'on':''}\" data-periodo=\"${v}\">${l}</button>`\n\n    return `<div class=\"wrap\">\n      <div class=\"top\">\n        <h1>Painel da URA de Voz</h1>\n        <span class=\"sub\">${n} chamadas${d2.state.desde?` · desde ${esc(new Date(d2.state.desde).toLocaleDateString('pt-BR',{day:'2-digit',month:'2-digit'}))}`:''}${d2.state.carregando?' · atualizando…':''}</span>\n        <span class=\"cresce\"></span>\n        <input data-campo=\"busca\" value=\"${esc(d2.state.busca)}\" placeholder=\"🔎 consultora / número / call_id\" style=\"width:210px\">\n        <div class=\"seg\">${seg(7,'7d')}${seg(30,'30d')}${seg(90,'90d')}${seg(0,'tudo')}</div>\n        <button class=\"gh\" data-refresh=\"1\">↻ atualizar</button>\n      </div>\n      <div class=\"filtros\">\n        ${combo('fDesfecho','desfecho: todos',optDesfecho)}\n        ${combo('fTransb','transbordo: todos',['sim','nao'],v=>v==='sim'?'transbordou':'ficou retida')}\n        ${combo('fMotivo','motivo: todos',optMotivo)}\n        ${combo('fServico','serviço consumido: todos',optServico,rotulo)}\n        ${combo('fCsat','CSAT: todos',['baixo','neutro','alto','sem'],v=>nCsat[v])}\n        <label class=\"chk\"><input type=\"checkbox\" data-filtro=\"fErro\" ${d2.state.fErro?'checked':''}> só com erro</label>\n        <span class=\"cresce\"></span>\n        <input type=\"date\" data-filtro=\"de\" class=\"${d2.state.de?'on':''}\" value=\"${esc(d2.state.de||'')}\" title=\"de\">\n        <input type=\"date\" data-filtro=\"ate\" class=\"${d2.state.ate?'on':''}\" value=\"${esc(d2.state.ate||'')}\" title=\"até\">\n        ${ativos?`<button class=\"gh\" data-limpar=\"1\">limpar ${ativos} filtro${ativos>1?'s':''}</button>`:''}\n      </div>\n      <div class=\"body\">\n        <div class=\"kpis\">\n          ${kpi(n,'Chamadas')}\n          ${kpi(dur(durMed),'Duração média')}\n          ${kpi(n?Math.round(retidas/n*100)+'%':'—','Retenção')}\n          ${kpi(n?Math.round(transf/n*100)+'%':'—','Pediram atendente')}\n          ${kpi(servicos.length,'Serviços consumidos')}\n          ${kpi(servicos.length?Math.round(erros/servicos.length*100)+'%':'—','Erros em serviços')}\n          ${kpi(consultores,'Consultoras únicas')}\n          ${kpi(csatMed!=null?csatMed.toFixed(1):'—','CSAT médio')}\n        </div>\n        <div class=\"grid2\">\n          <div class=\"card\"><h2>Chamadas por dia</h2>${colDia}</div>\n          <div class=\"card\"><h2>Desfecho</h2>${barras(porOutcome.map(r=>({k:r.k,v:r.v})),'#16a34a')}</div>\n        </div>\n        <div class=\"grid2b\">\n          <div class=\"card\"><h2>Por que transbordou <span class=\"h2sub\">— último serviço antes de pedir atendente (${transbordou.length})</span></h2>${barras(porMotivoT,'#c2410c')}</div>\n          <div class=\"card\"><h2>Por que ficou retida <span class=\"h2sub\">— último serviço da ligação (${ficouRetida.length})</span></h2>${barras(porMotivoR,'#16a34a')}</div>\n        </div>\n        <div class=\"grid2b\">\n          <div class=\"card\"><h2>Serviços mais consumidos <span class=\"h2sub\">(use cases)</span></h2>${barras(porUsecase,'#2b6cff')}</div>\n          <div class=\"card\"><h2>Chamadas por consultora</h2>${barras(porConsultor,'#7c5fd0')}</div>\n        </div>\n        <div class=\"card\"><h2>Buscas de FAQ <span class=\"h2sub\">— o que a consultora perguntou</span></h2>${barras(buscasFaq,'#0ea5a4')}</div>\n        <div class=\"card\">\n          <h2>Chamadas <span class=\"h2sub\">— clique para abrir a transcrição, serviços e gravação</span></h2>\n          <table class=\"tbl\"><thead><tr><th>Quando</th><th>Consultora</th><th>Desfecho</th><th>Motivo</th>\n            <th style=\"text-align:right\">Duração</th><th style=\"text-align:right\">Serviços</th><th style=\"text-align:right\">CSAT</th></tr></thead>\n            <tbody>${linhas || `<tr><td colspan=\"7\" class=\"vazio\">nenhuma chamada no período</td></tr>`}</tbody></table>\n        </div>\n      </div>\n      ${d2.state.sel ? this.renderDrawer(d2, esc, dur, outPill) : ''}\n    </div>`\n  },\n\n  renderDrawer(d2, esc, dur, outPill) {\n    const inner = () => {\n      if (d2.state.carregandoDet) return `<div class=\"vazio\">carregando…</div>`\n      const det = d2.state.det; if (!det) return `<div class=\"vazio\">não foi possível carregar o detalhe.</div>`\n      const c = det.chamada || {}; const tr = Array.isArray(det.transcript)?det.transcript:[]; const sv = Array.isArray(det.servicos)?det.servicos:[]\n      const cs = Array.isArray(det.consultas)?det.consultas:[]\n      const usados = new Set()\n      const casar = (s, i) => {\n        if (cs[i] && cs[i].selected_usecase === s.usecase && !usados.has(i)) { usados.add(i); return cs[i] }\n        const j = cs.findIndex((x,k) => !usados.has(k) && x.selected_usecase === s.usecase)\n        if (j >= 0) { usados.add(j); return cs[j] }\n        return null\n      }\n      const pars = (s) => { try { const p = JSON.parse(s.parametros || '{}'); return (p && typeof p === 'object' && Object.keys(p).length) ? p : null } catch (e) { return null } }\n      const perfil = (cs.find(x => x.profile) || {}).profile || null\n      const m = (v,l) => `<div class=\"m\"><div class=\"mv\">${v==null||v===''?'—':esc(v)}</div><div class=\"ml\">${esc(l)}</div></div>`\n      const acao = (t) => { try { if (t.tool_calls&&t.tool_calls[0]) { const raw=t.tool_calls[0].params_as_json||(t.tool_calls[0].tool_details&&t.tool_calls[0].tool_details.body); const p=typeof raw==='string'?JSON.parse(raw):raw; return (p&&(p.action||p.name))||null } } catch(e){} return null }\n      const eng = `<svg viewBox=\"0 0 24 24\" width=\"11\" height=\"11\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" style=\"vertical-align:-1px;margin-right:4px\"><circle cx=\"12\" cy=\"12\" r=\"3\"/><path d=\"M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 1 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.6 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 1 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.6a1.65 1.65 0 0 0 1-1.51V3a2 2 0 1 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 1 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z\"/></svg>`\n      const frase = (t) => { try { const raw=t.tool_calls[0].params_as_json; const p=typeof raw==='string'?JSON.parse(raw):raw; const ap=typeof p.actionParameters==='string'?JSON.parse(p.actionParameters):p.actionParameters; return (ap&&(ap.search_phrase||ap.searchPhrase))||null } catch(e){} return null }\n      const conv = tr.length ? tr.map(t => { const role=t.role||t.source||''; const msg=t.message||t.text||''; const a=acao(t); const f=frase(t)\n        if (a&&!msg) return `<div class=\"tl\">${eng}${esc(a)}${f?` · “${esc(f)}”`:''}</div>`; if (!msg&&!a) return ''\n        const u=/user|caller|cliente|consultora/i.test(role)\n        return `<div class=\"bub ${u?'u':'a'}\"><div class=\"q\">${u?'consultora':'assistente'}${a?' · '+esc(a):''}</div>${esc(msg)}</div>` }).join('') : `<div class=\"vazio\">sem transcrição.</div>`\n      const bloco = (n, nome, q, s) => {\n        const p = s ? pars(s) : null\n        const busca = p && (p.search_phrase || p.searchPhrase)\n        const resto = p ? Object.keys(p).filter(k => !/^search_?phrase$/i.test(k)) : []\n        const lat = (s && s.latencia_s != null) ? Math.round(s.latencia_s*10)/10 + 's' : ((q && q.duration != null) ? q.duration + 's' : '')\n        return `<div class=\"svc2\">\n          <div class=\"svc2h\"><span class=\"o\">${esc(n)}</span>\n            <b>${esc(nome||'—')}</b>\n            ${s && s.erro?'<span class=\"pill b\">erro</span>':''}\n            ${q && q.call_agent?'<span class=\"pill i\">pediu atendente</span>':''}\n            ${q && q.launch_chat_survey?'<span class=\"pill a\">pesquisa</span>':''}\n            <span class=\"lat\">${esc(lat)}</span></div>\n          ${busca?`<div class=\"kv\"><span class=\"k\">busca</span><span class=\"v\">“${esc(busca)}”</span></div>`:''}\n          ${q && q.selected_faq?`<div class=\"kv\"><span class=\"k\">FAQ</span><span class=\"v faq\">${esc(q.selected_faq)}</span></div>`:''}\n          ${resto.length?`<div class=\"kv\"><span class=\"k\">params</span><span class=\"v mono\">${esc(resto.map(k=>k+'='+p[k]).join(' · '))}</span></div>`:''}\n        </div>` }\n      const svc = sv.length ? sv.map((s,i) => bloco(s.ordem!=null?s.ordem+1:i+1, s.usecase, casar(s,i), s)).join('') : ''\n      const sobras = cs.filter((x,k) => !usados.has(k))\n      const svcExtra = sobras.map((x) => bloco('·', x.selected_usecase, x, null)).join('')\n      const svcTudo = (svc + svcExtra) || `<div class=\"vazio\">nenhum serviço.</div>`\n      const grav = d2.state.audioUrl\n        ? `<div class=\"grav\">Gravação da ligação ${d2.state.audioVia?`<span class=\"mono\">(${esc(d2.state.audioVia)})</span>`:''}<audio controls autoplay src=\"${esc(d2.state.audioUrl)}\"></audio></div>`\n        : `<div class=\"grav\"><div>Gravação — <span class=\"mono\">${esc(c.conversation_id||'sem id')}</span></div>\n            <div style=\"display:flex;gap:8px;margin-top:10px\">\n              <button class=\"btn\" data-ouvir=\"1\" ${d2.state.buscandoAudio?'disabled':''}>${d2.state.buscandoAudio?'buscando…':'▶ ouvir gravação'}</button>\n            </div>\n            ${d2.state.audioMsg?`<div class=\"err\">${esc(d2.state.audioMsg)}</div>`:''}</div>`\n      return `<div class=\"metg\">\n          ${m(dur(c.duration_s),'duração')}${m(c.csat_score,'CSAT')}${m(c.retention,'retenção')}\n          ${m((c.user_turns||0)+'/'+(c.agent_turns||0),'turnos')}${m(c.usecases_count,'serviços')}\n          ${m(c.tool_errors,'erros')}${m(c.ai_tokens,'tokens')}\n          ${m(c.transferred_done?'sim':(c.transferred_intent?'tentou':'não'),'transferência')}\n          ${m(c.consultant_id,'consultora')}${perfil?m(perfil,'perfil'):''}</div>\n        ${tr.length ? `<h2>Transcrição</h2><div class=\"conv\">${conv}</div>` : ''}\n        <h2>Serviços consumidos</h2>${svcTudo}\n        <h2>Gravação</h2>${grav}`\n    }\n    const c = (d2.state.det && d2.state.det.chamada) || {}\n    return `<div class=\"drawer\">\n      <div class=\"dh\">${outPill(c.outcome)}<span class=\"mono\">${esc(String(d2.state.sel||'').slice(0,24))}</span>\n        <button class=\"x\" data-fechar=\"1\">×</button></div>\n      <div class=\"dc\">${inner()}</div>\n    </div>`\n  }\n}"}},"rushBiz":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"rushBiz"}}}},"rushDetalhe":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"rushDetalhe"}}}},"rushAudio":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"rushAudio"}}}},"senhaParam":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"senhaParam"}}}}}}}}}}]}},{"reference":"ExternalComponentInstancesTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}},{"_id":"5f060d9b1ca96def42f3c40a","displayName":"Builder AuthStrategy","component":"data2-core@AuthenticationStrategy","system":"5e3b796d6228227ca637d0f4","params":{"credentials":[{"component":"data2-core-types@DataSetColumnReference","params":{"column":"email"}},{"component":"data2-core-types@DataSetColumnReference","params":{"column":"password"}},{"component":"data2-core-types@DataSetColumnReference","params":{"column":"status"}}],"verifier":{"component":"data2-core-types@DataSetReference","params":{"reference":"UsersDataSet"}},"sessionShared":{"component":"data2-core-primitives@Boolean","params":{"content":false}},"password":{"disabled":false,"component":"data2-core-types@DataSetColumnReference","params":{"column":"password"}},"email":{"disabled":false,"component":"data2-core-types@DataSetColumnReference","params":{"column":"email"}},"resetPassword":{"resetEmail":{"artboard":{"params":{"preventEvaluation":true,"reference":"nR6Q9d7qdaPtZhJ28Q8Dy"},"component":"data2-core-types@ArtboardReference"}},"resetSuccessEmail":{"artboard":{"params":{"preventEvaluation":true,"reference":"yPCJBTKo8q1Mw0B_3Kpwl"},"component":"data2-core-types@ArtboardReference"}}},"session":[{"component":"data2-core-types@DataSetColumnReference","params":{"column":"name"}},{"component":"data2-core-types@DataSetColumnReference","params":{"column":"email"}},{"component":"data2-core-types@DataSetColumnReference","params":{"column":"password"}},{"component":"data2-core-types@DataSetColumnReference","params":{"column":"status"}}],"sessionMetadata":{"params":{"actions":[{"params":{"compare":{"params":{"subject":{"params":{"components":[{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$user._id"}}}},{"params":{"comparisonSubject":{"component":"data2-core-primitives@String","params":{"content":"active"}},"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$user.status"}}}}},"component":"data2-core-comparison-operators@Equal","displayName":"is active user"}]},"component":"data2-core-logical-operators@And","displayName":"is active and has _id"}},"component":"data2-core-logical-operators@Not"},"then":{"params":{"message":{"component":"data2-core-primitives@String","params":{"content":"Invalid Credentials"}},"code":{"component":"data2-core-primitives@String","params":{"content":"401"}}},"component":"data2-core-types@Error"}},"component":"data2-core-flow@If","displayName":"check $user"},{"params":{"action":{"params":{"pattern":{"component":"data2-core-primitives@String","params":{"content":"@data2.app"}},"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$user.email"}}}}},"component":"data2-core-functions@Includes","displayName":"isData2User"},"resultName":{"component":"data2-core-primitives@String","params":{"content":"is_data2_user"}}},"component":"data2-core-flow@Step"},{"params":{"action":{"params":{"pattern":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$metadata.d2_app"}}}},"subject":[{"component":"data2-core-primitives@String","params":{"content":"management"}}]},"component":"data2-core-functions@Includes","displayName":"isData2App Exception"},"resultName":{"component":"data2-core-primitives@String","params":{"content":"is_data2_app_exception"}}},"component":"data2-core-flow@Step"},{"params":{"resultName":{"component":"data2-core-primitives@String","params":{"content":"has_access"}},"action":{"params":{"compare":{"params":{"components":[{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"is_data2_user"}}}},{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"is_data2_app_exception"}}}}]},"component":"data2-core-logical-operators@Or"},"else":{"params":{"actions":[{"params":{"actions":[{"params":{"compare":{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$metadata.system"}}}}},"component":"data2-core-logical-operators@Not"},"then":{"params":{"message":{"component":"data2-core-primitives@String","params":{"content":"Invalid Credentials"}},"code":{"component":"data2-core-primitives@String","params":{"content":"401"}}},"component":"data2-core-types@Error"}},"component":"data2-core-flow@If","displayName":"check $metadata.system"},{"params":{"module":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"WuUa-VKHhW9uYU0E3-mJ8"}},"prop":"filter","args":[{"_id":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$metadata.system"}}}}}]},"component":"data2-core-types@Interaction"},{"params":{"compare":{"params":{"subject":{"component":"data2-core-types@Getter","params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"WuUa-VKHhW9uYU0E3-mJ8"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records.length"}}}}},"component":"data2-core-logical-operators@Not","displayName":"systems not found"},"then":{"params":{"message":{"component":"data2-core-primitives@String","params":{"content":"Invalid Credentials"}},"code":{"component":"data2-core-primitives@String","params":{"content":"401"}}},"component":"data2-core-types@Error"}},"component":"data2-core-flow@If","displayName":"check system found"},{"params":{"actions":[{"params":{"module":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"Ok6Tsu5XkS7r8PyW_7TyW"}},"prop":"filter","args":[{"user":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$user._id"}}}},"organization":{"component":"data2-core-types@Getter","params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"WuUa-VKHhW9uYU0E3-mJ8"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"cursor.current.organization._id"}}}}}]},"component":"data2-core-types@Interaction"},{"params":{"module":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"NqKAY34n2ffFL64zamp7t"}},"prop":"filter","args":[{"user":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$user._id"}}}},"system":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$metadata.system"}}}}}]},"component":"data2-core-types@Interaction"}]},"component":"data2-core-flow@Parallel"}]},"component":"data2-core-flow@Series","displayName":"common setup"},{"params":{"then":{"params":{"content":true},"component":"data2-core-primitives@Boolean"},"else":{"params":{"content":false},"component":"data2-core-primitives@Boolean"},"compare":{"params":{"components":{"params":{"array":{"params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"WuUa-VKHhW9uYU0E3-mJ8"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records"}}},"component":"data2-core-types@Getter"},"mapper":{"disabled":false,"component":"data2-core-functions@Function","params":{"parameters":[{"component":"data2-core-primitives@String","params":{"content":"system"}}],"body":{"params":{"expression":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"system.visibility"}}}},"defaultCase":{"params":{"content":false},"component":"data2-core-primitives@Boolean"},"cases":[{"component":"data2-core-flow@Case","params":{"expression":{"component":"data2-core-primitives@String","params":{"content":"public"}},"value":{"params":{"components":[{"params":{"array":{"params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"Ok6Tsu5XkS7r8PyW_7TyW"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records"}}},"component":"data2-core-types@Getter"},"filter":{"disabled":false,"component":"data2-core-functions@Function","params":{"parameters":[{"component":"data2-core-primitives@String","params":{"content":"member"}}],"body":{"params":{"components":[{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"member.organization._id"}}}},"comparisonSubject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"system.organization._id"}}}}},"component":"data2-core-comparison-operators@Equal","displayName":"is org member"},{"params":{"pattern":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"member.role"}}}},"subject":[{"component":"data2-core-primitives@String","params":{"content":"owner"}},{"component":"data2-core-primitives@String","params":{"content":"billing-owner"}},{"component":"data2-core-primitives@String","params":{"content":"admin"}},{"component":"data2-core-primitives@String","params":{"content":"member"}}]},"component":"data2-core-functions@Includes","displayName":"has role with access"}]},"component":"data2-core-logical-operators@And","displayName":"has org member role with access"}}}},"component":"data2-core-functions@ArrayFind","displayName":"is org member"},{"params":{"components":[{"params":{"array":{"params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"Ok6Tsu5XkS7r8PyW_7TyW"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records"}}},"component":"data2-core-types@Getter"},"filter":{"disabled":false,"component":"data2-core-functions@Function","params":{"parameters":[{"component":"data2-core-primitives@String","params":{"content":"member"}}],"body":{"params":{"components":[{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"member.organization._id"}}}},"comparisonSubject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"system.organization._id"}}}}},"component":"data2-core-comparison-operators@Equal","displayName":"is org member"},{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"member.role"}}}},"comparisonSubject":{"component":"data2-core-primitives@String","params":{"content":"collaborator"}}},"component":"data2-core-comparison-operators@Equal","displayName":"is collaborator"}]},"component":"data2-core-logical-operators@And","displayName":"is org member and collaborator"}}}},"component":"data2-core-functions@ArrayFind","displayName":"is collaborator"},{"params":{"array":{"params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"NqKAY34n2ffFL64zamp7t"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records"}}},"component":"data2-core-types@Getter"},"filter":{"disabled":false,"component":"data2-core-functions@Function","params":{"parameters":[{"component":"data2-core-primitives@String","params":{"content":"access"}}],"body":{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"access.system._id"}}}},"comparisonSubject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"system._id"}}}}},"component":"data2-core-comparison-operators@Equal"}}}},"component":"data2-core-functions@ArrayFind","displayName":"has system access"}]},"component":"data2-core-logical-operators@And","displayName":"is collaborator and has system access"}]},"component":"data2-core-logical-operators@Or"}}},{"component":"data2-core-flow@Case","params":{"expression":{"component":"data2-core-primitives@String","params":{"content":"private"}},"value":{"params":{"components":[{"params":{"array":{"params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"Ok6Tsu5XkS7r8PyW_7TyW"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records"}}},"component":"data2-core-types@Getter"},"filter":{"disabled":false,"component":"data2-core-functions@Function","params":{"parameters":[{"component":"data2-core-primitives@String","params":{"content":"member"}}],"body":{"params":{"components":[{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"member.organization._id"}}}},"comparisonSubject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"system.organization._id"}}}}},"component":"data2-core-comparison-operators@Equal","displayName":"is org member"},{"params":{"pattern":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"member.role"}}}},"subject":[{"component":"data2-core-primitives@String","params":{"content":"owner"}},{"component":"data2-core-primitives@String","params":{"content":"admin"}}]},"component":"data2-core-functions@Includes","displayName":"is owner/admin"}]},"component":"data2-core-logical-operators@And","displayName":"has org member role with access"}}}},"component":"data2-core-functions@ArrayFind","displayName":"is owner/admin"},{"params":{"components":[{"params":{"array":{"params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"Ok6Tsu5XkS7r8PyW_7TyW"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records"}}},"component":"data2-core-types@Getter"},"filter":{"disabled":false,"component":"data2-core-functions@Function","params":{"parameters":[{"component":"data2-core-primitives@String","params":{"content":"member"}}],"body":{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"member.organization._id"}}}},"comparisonSubject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"system.organization._id"}}}}},"component":"data2-core-comparison-operators@Equal"}}}},"component":"data2-core-functions@ArrayFind","displayName":"is org member"},{"params":{"array":{"params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"NqKAY34n2ffFL64zamp7t"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records"}}},"component":"data2-core-types@Getter"},"filter":{"disabled":false,"component":"data2-core-functions@Function","params":{"parameters":[{"component":"data2-core-primitives@String","params":{"content":"access"}}],"body":{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"access.system._id"}}}},"comparisonSubject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"system._id"}}}}},"component":"data2-core-comparison-operators@Equal"}}}},"component":"data2-core-functions@ArrayFind","displayName":"has system access"}]},"component":"data2-core-logical-operators@And","displayName":"is member and has system access"}]},"component":"data2-core-logical-operators@Or","displayName":"is owner/admin or has system access"}}}]},"component":"data2-core-flow@Switch"}}}},"component":"data2-core-functions@ArrayMap"}},"component":"data2-core-logical-operators@And","displayName":"has org member for all systems in request"}},"component":"data2-core-flow@If"}]},"component":"data2-core-flow@Series"},"then":{"params":{"content":true},"component":"data2-core-primitives@Boolean"}},"component":"data2-core-flow@If"}},"component":"data2-core-flow@Step"},{"params":{"then":{"system":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$metadata.system"}}}}},"else":{"params":{"message":{"component":"data2-core-primitives@String","params":{"content":"Invalid Credentials"}},"code":{"component":"data2-core-primitives@String","params":{"content":"401"}}},"component":"data2-core-types@Error"},"compare":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"has_access"}}}}},"component":"data2-core-flow@If"}]},"component":"data2-core-flow@Series"},"sessionExpiresIn":{"component":"data2-core-primitives@Number","params":{"content":604800}},"sessionRenewal":{"component":"data2-core-primitives@Boolean","params":{"content":true}},"identityProviders":[{"provider":{"component":"data2-core-primitives@String","params":{"content":"GOOGLE"}},"clientId":{"component":"data2-core-primitives@String","params":{"content":"55470861603-22pvfc1t49lp8cjnbfk71s5ct9hp1q1v.apps.googleusercontent.com"}}}]},"reference":"Data2BuilderAuthentication","updatedAt":"2026-08-06T19:31:23.557Z"},{"_id":"5f060ee51ca96def42f3fbd1","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"UsersTableDataSource"}},"pagination":{"size":{"component":"data2-core-primitives@Number","params":{"content":1}}}},"reference":"UsersDataSet","updatedAt":"2026-03-04T21:16:40.291Z"},{"_id":"68f938cf5fb002e33752a43d","displayName":"InternalApplicationsAuthenticationStrategy","component":"data2-core@AuthenticationStrategy","system":"5e3b796d6228227ca637d0f4","params":{"resetPassword":{"resetEmail":{"artboard":{"params":{"preventEvaluation":true},"component":"data2-core-types@ArtboardReference"}},"resetSuccessEmail":{"artboard":{"params":{"preventEvaluation":true},"component":"data2-core-types@ArtboardReference"}}},"verifier":{"disabled":false,"component":"data2-core-types@DataSetReference","params":{"reference":"InternalApplicationsVerifierDataSet"}},"password":{"disabled":false,"component":"data2-core-types@DataSetColumnReference","params":{"column":"secret"}},"credentials":[{"component":"data2-core-types@FieldReference","params":{"key":"application"}},{"component":"data2-core-types@FieldReference","params":{"key":"secret"}},{"component":"data2-core-types@FieldReference","params":{"key":"status"}}],"session":[{"component":"data2-core-types@FieldReference","params":{"key":"application"}},{"component":"data2-core-types@FieldReference","params":{"key":"secret"}},{"component":"data2-core-types@FieldReference","params":{"key":"status"}}]},"reference":"InternalApplicationsAuthenticationStrategy","updatedAt":"2025-10-22T20:08:54.442Z","createdAt":"2025-10-22T20:04:31.198Z"},{"component":"data2-core@DataSet","reference":"SessionsDataSet","params":{"requiresConfirmation":false,"pk":"key","datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"data2sessionsdatasource"}}}},{"reference":"UsersTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}},{"component":"data2-core@ServerSessionsDataSource","reference":"data2sessionsdatasource","params":{}},{"_id":"5f1600b70528b92601890f75","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"ServiceComponentInstancesTableDataSource"}},"pk":"_id","autoEdit":true,"master":{"params":{"dataset":{"component":"data2-core-types@DataSetReference","params":{"reference":"SystemsDataSet"}},"localField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"system"}},"masterField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"_id"}}},"component":"data2-core-types@DataSetMaster"},"autoSelect":{"component":"data2-core-primitives@Boolean","params":{"content":false}},"policies":{"params":{"allowGet":{"component":"data2-core-primitives@Boolean","params":{"content":true}}},"component":"data2@hkXy-UhMjhyfptdE6_fpW"}},"reference":"ServiceComponentInstancesDataSet","updatedAt":"2025-11-06T21:07:20.208Z","createdAt":"2020-07-20T20:38:15.436Z"},{"_id":"5f1602190528b92601890f7d","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"pagination":{"size":40000},"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"ComponentInstancesTableDataSource"}},"sort":{"_id":1},"pk":"_id","autoEdit":true,"master":{"dataset":{"component":"data2-core-types@DataSetReference","params":{"reference":"SystemsDataSet"}},"localField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"system"}},"masterField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"_id"}}},"autoSelect":{"component":"data2-core-primitives@Boolean","params":{"content":false}},"policies":{"params":{"allowGet":{"component":"data2-core-primitives@Boolean","params":{"content":true}}},"component":"data2@hkXy-UhMjhyfptdE6_fpW"}},"reference":"ComponentInstancesDataSet","updatedAt":"2025-11-06T21:07:07.089Z","createdAt":"2020-07-20T20:44:09.350Z"},{"_id":"5f16017f0528b92601890f7a","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"FileComponentInstancesTableDataSource"}},"pk":"_id","autoEdit":true,"filter":{"domain":{"component":"data2-core-types@Reference","params":{"reference":"clientApplicationDomain"}}},"master":{"params":{"dataset":{"component":"data2-core-types@DataSetReference","params":{"reference":"SystemsDataSet"}},"localField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"system"}},"masterField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"_id"}}},"component":"data2-core-types@DataSetMaster"},"autoSelect":{"component":"data2-core-primitives@Boolean","params":{"content":false}}},"reference":"FileComponentInstancesDataSet","updatedAt":"2023-08-03T16:08:18.193Z","createdAt":"2020-07-20T20:41:35.797Z"},{"_id":"5f1601990528b92601890f7b","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"ApplicationsTableDataSource"}},"pk":"_id","autoEdit":{"component":"data2-core-primitives@Boolean","params":{"content":true}},"master":{"dataset":{"component":"data2-core-types@DataSetReference","params":{"reference":"SystemsDataSet"}},"localField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"system"}},"masterField":{"params":{"column":"_id"},"component":"data2-core-types@DataSetColumnReference"}},"autoSelect":{"component":"data2-core-primitives@Boolean","params":{"content":false}}},"reference":"ApplicationsDataSet","updatedAt":"2022-12-13T21:51:13.995Z","createdAt":"2020-07-20T20:42:01.657Z"},{"_id":"5f16172a0528b92601890f85","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"pagination":{"size":{"component":"data2-core-primitives@Number","params":{"content":10000}}},"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"DataComponentInstancesTableDataSource"}},"pk":"_id","autoEdit":true,"master":{"params":{"dataset":{"component":"data2-core-types@DataSetReference","params":{"reference":"SystemsDataSet"}},"localField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"system"}},"masterField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"_id"}}},"component":"data2-core-types@DataSetMaster"},"autoSelect":{"component":"data2-core-primitives@Boolean","params":{"content":false}},"policies":{"params":{"allowGet":{"component":"data2-core-primitives@Boolean","params":{"content":true}}},"component":"data2@hkXy-UhMjhyfptdE6_fpW"}},"reference":"DataComponentInstancesDataSet","updatedAt":"2025-11-06T21:07:12.831Z","createdAt":"2020-07-20T22:14:02.097Z"},{"_id":"60ddc1bbda3861ca65407c5a","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"FileComponentInstancesTableDataSource"}},"pk":"_id","autoEdit":true,"filter":{"domain":{"component":"data2-core-types@Reference","params":{"reference":"clientApplicationDomain"}}},"pagination":{"size":1},"master":{"params":{"dataset":{"component":"data2-core-types@DataSetReference","params":{"reference":"SystemsDataSet"}},"localField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"system"}},"masterField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"_id"}}},"component":"data2-core-types@DataSetMaster"}},"reference":"OneFileComponentInstancesDataSet","updatedAt":"2022-02-09T19:44:38.036Z","createdAt":"2020-07-20T20:41:35.797Z"},{"_id":"63ee3d38b8c9838c62fb05fb","displayName":"DriveFileComponentInstancesDataSet","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"FileComponentInstancesTableDataSource"}},"pk":"_id","autoEdit":true,"filter":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core@Drive"}}},"master":{"params":{"dataset":{"component":"data2-core-types@DataSetReference","params":{"reference":"SystemsDataSet"}},"localField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"system"}},"masterField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"_id"}}},"component":"data2-core-types@DataSetMaster"},"autoSelect":{"component":"data2-core-primitives@Boolean","params":{"content":false}}},"reference":"DriveFileComponentInstancesDataSet","updatedAt":"2025-12-04T21:58:32.367Z","createdAt":"2023-02-16T14:27:07.797Z"},{"reference":"ServiceComponentInstancesTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[{"_id":"6a74bb0e5c63d915f8faebbf","component":"data2-rush@HTTPListener","reference":"poc-ura-metricas","system":"65a86528721f5a092783289d","params":{}},{"_id":"6a74dcd42df620b15d6e5ea4","component":"data2-rush@HTTPListener","reference":"poc-ura-live","system":"65a86528721f5a092783289d","params":{}},{"_id":"6a74dcd8464ed66661355cf3","component":"data2-rush@HTTPListener","reference":"poc-ura-signal","system":"65a86528721f5a092783289d","params":{}},{"_id":"6a75be1ae45e25fa9e454cca","component":"data2-rush@HTTPListener","reference":"poc-ura-ctrl","system":"65a86528721f5a092783289d","params":{}},{"_id":"6a8ec75b42a2cc71dcbb6504","component":"data2-rush@HTTPListener","reference":"poc-ura-monitor","system":"65a86528721f5a092783289d","params":{},"deleteMetadata":{}},{"_id":"6a8ecdc92b84be32946c592e","component":"data2-rush@HTTPListener","reference":"poc-ura-biz","system":"65a86528721f5a092783289d","params":{},"deleteMetadata":{}},{"_id":"6a8ecf97d880252a7e5a8f16","component":"data2-rush@HTTPListener","reference":"poc-ura-audio","system":"65a86528721f5a092783289d","params":{},"deleteMetadata":{}}]}},{"reference":"ComponentInstancesTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[{"_id":"6a75bb48e45e25fa9e45494b","component":"data2-core-primitives@Artboard","system":"65a86528721f5a092783289d","displayName":"URA — Console de chamadas","params":{"title":{"component":"data2-core-primitives@String","params":{"content":"URA — Console de chamadas"}},"dimensions":{"component":"data2-core-types@Dimensions","params":{"width":1440,"height":900}},"position":{"component":"data2-core-types@Position","params":{"x":-1344,"y":2358,"exactX":-1343.7099999999998,"exactY":2357.7}}},"reference":"ysrvk7_jJr6modefaQFX5"},{"_id":"2e94dbcdaec388abfa8439bf","parent":"6a75bb48e45e25fa9e45494b","component":"natura-br@kUraConsole","system":"65a86528721f5a092783289d","displayName":"console","params":{"ddrPadrao":{"component":"data2-core-primitives@String","params":{"content":"+551148905654"}},"rushLive":{"component":"data2-core-primitives@String","params":{"content":"poc-ura-live"}},"rushSinal":{"component":"data2-core-primitives@String","params":{"content":"poc-ura-signal"}},"super":{"position":{"component":"data2-core-types@Position","params":{"x":0,"y":0,"freeTransform":true,"left":true,"right":true,"top":true,"bottom":true}},"dimensions":{"component":"data2-core-types@Dimensions","params":{"width":1440,"height":900}}}},"reference":"PVRBbEP_mXqH1zBuDFU3x"}]}},{"_id":"5fc4faeec8da12be8bf3d08a","component":"data2-core@Data2TableDataSource","system":"5e3b796d6228227ca637d0f4","params":{"paginate":{"default":1000},"super":{},"table":"fileComponentInstances","schema":[{"component":"data2-core-types@Field","params":{"key":"component","title":"Component","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"system","title":"System","component":"data2-core-primitives@ObjectId"}},{"component":"data2-core-types@Field","params":{"key":"displayName","title":"Display Name","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"params","title":"Params","component":"data2-core-primitives@Object"}},{"component":"data2-core-types@Field","params":{"key":"reference","title":"Reference","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":{"component":"data2-core-primitives@String","params":{"content":"fileId"}},"title":"File ID","component":"data2-core-primitives@ObjectId"},"break":true}],"hooks":{"before":{"all":[{"hook":"softDelete"}],"find":[{"hook":"useCache","options":{"additionalGroupKeys":["$and.0.system","$and.1.system"],"skipCache":true}}],"create":[{"hook":"timestamp","options":{"fields":["createdAt","updatedAt"]}}],"update":[{"hook":"timestamp","options":{"fields":["updatedAt"]}}],"patch":[{"hook":"timestamp","options":{"fields":["updatedAt"]}}]},"after":{"all":[{"hook":"fileUrl","options":{"downloadField":"params.fileUrl","uploadField":"params.uploadFileUrl","enhanceSchema":false}}],"find":[{"hook":"useCache","options":{"additionalGroupKeys":["$and.0.system","$and.1.system"],"skipCache":true}}],"create":[{"hook":"invalidateCache","options":{"additionalGroupKeys":["system"],"secondaryUniqueKeys":["reference"]}},{"hook":"invalidateEnvCache"}],"update":[{"hook":"invalidateCache","options":{"additionalGroupKeys":["system"],"secondaryUniqueKeys":["reference"]}},{"hook":"invalidateEnvCache"}],"patch":[{"hook":"invalidateCache","options":{"additionalGroupKeys":["system"],"secondaryUniqueKeys":["reference"]}},{"hook":"invalidateEnvCache"}],"remove":[{"hook":"invalidateCache","options":{"additionalGroupKeys":["system"],"secondaryUniqueKeys":["reference"]}},{"hook":"invalidateEnvCache"}]}},"multi":["create"],"useEstimatedDocumentCount":true},"reference":"FileComponentInstancesTableDataSource","updatedAt":"2022-12-19T20:42:51.837Z","createdAt":"2020-11-30T13:59:52.294Z"},{"reference":"ApplicationsTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[{"_id":"6a75da42e45e25fa9e4576ba","displayName":"Console URA de voz","reference":"Wlt6idug5xhUGYrImiSIf","params":{"domain":{"component":"data2-core-primitives@String","params":{"content":"ura-console"}},"entryPoint":{"component":"data2-core-types@ComponentInstanceReference","params":{"component":"data2-core-primitives@Artboard","reference":"ysrvk7_jJr6modefaQFX5"}}},"system":"65a86528721f5a092783289d","component":"data2-core-primitives@Application"}]}},{"reference":"DataComponentInstancesTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}},{"reference":"BundlesTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}}],"processesParams":{}}}}