專案

一般

配置概況

工作單 #199 » P9801010-3DS Result Overview.json

marlboro chu, 2025-06-30 02:03

 
{
"result" : {
"id" : 618,
"orgId" : 1,
"folderId" : 136,
"folderUid" : "bemeqa2prz7k0d",
"uid" : "fen3y5c3e8q2of",
"name" : "P9801010-3DS Result Overview",
"kind" : 1,
"type" : "marcusolsson-dynamictext-panel",
"description" : "Provides a clearer understanding that the section summarizes the results of 3D Secure authentication.",
"model" : {
"datasource" : {
"type" : "elasticsearch",
"uid" : "f45b49c9-ba56-43c8-b278-a87adffdb57c"
},
"description" : "Provides a clearer understanding that the section summarizes the results of 3D Secure authentication.",
"fieldConfig" : {
"defaults" : {
"thresholds" : {
"mode" : "absolute",
"steps" : [ {
"color" : "green",
"value" : null
} ]
},
"unit" : "locale"
},
"overrides" : [ ]
},
"gridPos" : {
"h" : 9,
"w" : 16,
"x" : 8,
"y" : 1
},
"id" : 9801010,
"options" : {
"afterRender" : "let currentSortField = 'Count';\r\nlet currentSortOrder = 'desc';\r\n\r\nfunction generate3DSResultItems(data, sortField = 'Count', sortOrder = 'desc') {\r\n const tableBody = document.getElementById('dsresult-table-body');\r\n const tableFooter = document.getElementById('dsresult-table-footer');\r\n tableBody.innerHTML = '';\r\n tableFooter.innerHTML = '';\r\n\r\n // 計算筆數總和(數字型)\r\n const totalCount = data.reduce((acc, item) => {\r\n const num = Number(String(item.Count).replace(/,/g, ''));\r\n return acc + (isNaN(num) ? 0 : num);\r\n }, 0);\r\n\r\n // 排序\r\n data.sort((a, b) => {\r\n let valA = a[sortField];\r\n let valB = b[sortField];\r\n\r\n const numA = Number(String(valA).replace(/,/g, ''));\r\n const numB = Number(String(valB).replace(/,/g, ''));\r\n\r\n if (!isNaN(numA) && !isNaN(numB)) {\r\n return sortOrder === 'asc' ? numA - numB : numB - numA;\r\n } else {\r\n return sortOrder === 'asc' ? String(valA).localeCompare(String(valB)) : String(valB).localeCompare(String(valA));\r\n }\r\n });\r\n\r\n // 產生資料列\r\n data.forEach(item => {\r\n const tr = document.createElement('tr');\r\n\r\n // 交易狀態欄\r\n const tdStatus = document.createElement('td');\r\n tdStatus.textContent = item.feedbackInfo__transStatus;\r\n tr.appendChild(tdStatus);\r\n\r\n // 筆數欄\r\n const tdCount = document.createElement('td');\r\n tdCount.textContent = item.Count;\r\n tr.appendChild(tdCount);\r\n\r\n // 百分比欄\r\n const tdPercent = document.createElement('td');\r\n const numCount = Number(String(item.Count).replace(/,/g, ''));\r\n let percentText = totalCount > 0 ? ((numCount / totalCount) * 100).toFixed(2) + '%' : '0.00%';\r\n tdPercent.textContent = percentText;\r\n tr.appendChild(tdPercent);\r\n\r\n tableBody.appendChild(tr);\r\n });\r\n\r\n // 產生表尾加總\r\n const footerRow = document.createElement('tr');\r\n\r\n // 合併前兩欄 (狀態欄顯示 Total)\r\n const totalLabelCell = document.createElement('td');\r\n totalLabelCell.textContent = 'Total';\r\n totalLabelCell.colSpan = 1;\r\n totalLabelCell.style.textAlign = 'left';\r\n footerRow.appendChild(totalLabelCell);\r\n\r\n // 筆數總和欄\r\n const totalCountCell = document.createElement('td');\r\n totalCountCell.textContent = totalCount.toLocaleString();\r\n footerRow.appendChild(totalCountCell);\r\n\r\n // 百分比欄空白\r\n const emptyCell = document.createElement('td');\r\n emptyCell.textContent = '';\r\n footerRow.appendChild(emptyCell);\r\n\r\n tableFooter.appendChild(footerRow);\r\n}\r\n\r\nfunction setup3DSResultTableHeader(data) {\r\n const tableHeader = document.getElementById('dsresult-table-header');\r\n tableHeader.innerHTML = '';\r\n\r\n const headers = [\r\n { text: 'Transaction Status', field: 'feedbackInfo__transStatus' },\r\n { text: 'Volume', field: 'Count' },\r\n { text: 'Percentage', field: 'Percent' } // 注意這是虛擬欄位,不在原始資料裡\r\n ];\r\n\r\n const headerRow = document.createElement('tr');\r\n\r\n headers.forEach(header => {\r\n const th = document.createElement('th');\r\n th.textContent = header.text;\r\n th.style.cursor = header.field !== 'Percent' ? 'pointer' : 'default'; // 百分比欄不可排序\r\n th.dataset.field = header.field;\r\n\r\n if (header.field !== 'Percent') {\r\n th.addEventListener('click', () => {\r\n if (currentSortField === header.field) {\r\n currentSortOrder = currentSortOrder === 'asc' ? 'desc' : 'asc';\r\n } else {\r\n currentSortField = header.field;\r\n currentSortOrder = 'asc';\r\n }\r\n generate3DSResultItems(data, currentSortField, currentSortOrder);\r\n updateTableHeaderSortIndicator(headers);\r\n });\r\n }\r\n\r\n headerRow.appendChild(th);\r\n });\r\n\r\n tableHeader.appendChild(headerRow);\r\n}\r\n\r\nfunction updateTableHeaderSortIndicator(headers) {\r\n const ths = document.querySelectorAll('#dsresult-table-header th');\r\n ths.forEach(th => {\r\n const field = th.dataset.field;\r\n const header = headers.find(h => h.field === field);\r\n if (!header) return;\r\n\r\n if (field === currentSortField) {\r\n th.textContent = `${header.text} ${currentSortOrder === 'asc' ? '▲' : '▼'}`;\r\n } else {\r\n th.textContent = header.text;\r\n }\r\n });\r\n}\r\n\r\n// 初始化\r\nif (Array.isArray(context.data) && context.data.length > 0 && Array.isArray(context.data[0])) {\r\n const tableData = context.data[0];\r\n setup3DSResultTableHeader(tableData);\r\n generate3DSResultItems(tableData);\r\n}\r\n",
"content" : "<div class=\"dsresult-table-container\">\n <table class=\"dsresult-table\">\n <thead id=\"dsresult-table-header\"></thead> \n <tbody id=\"dsresult-table-body\"></tbody>\n </table>\n <p>\n <table class=\"dsresult-table-footer dsresult-table\">\n <tfoot id=\"dsresult-table-footer\"></tfoot>\n </table>\n</div>\n",
"contentPartials" : [ ],
"defaultContent" : "The query didn't return any results.",
"editor" : {
"format" : "auto",
"language" : "markdown"
},
"editors" : [ "afterRender", "styles" ],
"externalStyles" : [ ],
"helpers" : "",
"renderMode" : "data",
"styles" : "\r\n/* 表格本體 */\r\n.dsresult-table {\r\n border-collapse: collapse;\r\n width: 100%;\r\n //font-family: 'Segoe UI', 'Roboto Mono', 'Noto Sans TC', monospace;\r\n table-layout: fixed;\r\n}\r\n\r\n.dsresult-table-footer {\r\n position: sticky;\r\n bottom: 0;\r\n z-index: 3;\r\n //pointer-events: none; /* 若不需互動 */\r\n backdrop-filter: blur(15px);\r\n}\r\n\r\n/* 固定 header 和 footer */\r\n.dsresult-table thead th, .dsresult-table tfoot td {\r\n position: sticky;\r\n z-index: 2;\r\n}\r\n\r\n/* 表頭固定在頂部 */\r\n.dsresult-table thead th {\r\n top: 0;\r\n text-align: center;\r\n //padding: 8px;\r\n //font-weight: bold;\r\n //border-bottom: 1px solid rgba(100, 181, 246, 0.4);\r\n}\r\n\r\n/* 表尾固定在底部 */\r\n.dsresult-table tfoot td {\r\n bottom: 0;\r\n //font-weight: bold;\r\n //padding: 8px;\r\n //border-top: 1px solid rgba(100, 181, 246, 0.4);\r\n}\r\n\r\n.dsresult-table td:first-child,\r\n.dsresult-table th:first-child {\r\n width: 20%;\r\n}\r\n\r\n/* 表格內容欄位 */\r\n.dsresult-table tbody td {\r\n padding: 8px;\r\n //font-size: 16px;\r\n //color: #e0f7fa;\r\n //border-bottom: 1px solid rgba(100, 181, 246, 0.1);\r\n}\r\n\r\n/* 交易狀態置中 */\r\n.dsresult-table tbody td:first-child {\r\n text-align: center;\r\n}\r\n\r\n.dsresult-table tfoot td:first-child{\r\n text-align: left;\r\n}\r\n/* 筆數與百分比靠右 */\r\n.dsresult-table tbody td:nth-child(2),\r\n.dsresult-table tfoot td:nth-child(2),\r\n.dsresult-table tbody td:nth-child(3),\r\n.dsresult-table tfoot td:nth-child(3) {\r\n text-align: right;\r\n font-variant-numeric: tabular-nums;\r\n}\r\n\r\n/* 滑鼠提示效果 */\r\n.dsresult-table thead th {\r\n cursor: pointer;\r\n}\r\n\r\n.dsresult-table-footer, .dsresult-table-footer td {\r\n border: none;\r\n}\r\n.dsresult-table-footer {\r\n border-top: 1px solid rgba(204, 204, 220, 0.2);;\r\n}\r\n\r\n/* hover 效果 */\r\n.dsresult-table tbody tr:hover {\r\n //background-color: rgba(66, 165, 245, 0.1);\r\n}\r\n",
"wrap" : true
},
"pluginVersion" : "5.7.0",
"targets" : [ {
"alias" : "",
"bucketAggs" : [ {
"field" : "feedbackInfo__transStatus",
"id" : "2",
"settings" : {
"min_doc_count" : "1",
"missing" : "nan",
"order" : "desc",
"orderBy" : "_term",
"size" : "0"
},
"type" : "terms"
} ],
"datasource" : {
"type" : "elasticsearch",
"uid" : "f45b49c9-ba56-43c8-b278-a87adffdb57c"
},
"metrics" : [ {
"id" : "1",
"type" : "count"
} ],
"query" : "${process_query:raw}",
"refId" : "A",
"timeField" : "create_datetime"
} ],
"title" : "A-07. 3DS Authentication Outcome Breakdown",
"type" : "marcusolsson-dynamictext-panel"
},
"version" : 39,
"meta" : {
"folderName" : "R98-Library",
"folderUid" : "bemeqa2prz7k0d",
"connectedDashboards" : 4,
"created" : "2025-05-27T10:43:32+08:00",
"updated" : "2025-06-04T17:48:05+08:00",
"createdBy" : {
"avatarUrl" : "/avatar/ce6412d58e966caaa26cac12eb99734b",
"id" : 1,
"name" : "admin"
},
"updatedBy" : {
"avatarUrl" : "/avatar/ce6412d58e966caaa26cac12eb99734b",
"id" : 1,
"name" : "admin"
}
}
}
}
(15-15/30)