Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions index.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,12 @@
"download_url": "https://raw.githubusercontent.com/amidaware/reporting-templates/master/templates/Agent%20Specs.json"
},
{
"name": "Agent TRMM Install date_descending v1.3",
"download_url": "https://raw.githubusercontent.com/amidaware/reporting-templates/master/templates/Agent%20TRMM%20Install%20date_descending%20v1.3.json"
"name": "Agent TRMM Install date_by Client descending v1.5",
"download_url": "https://raw.githubusercontent.com/amidaware/reporting-templates/master/templates/Agent%20TRMM%20Install%20date_by%20Client%20descending%20v1.5.json"
},
{
"name": "Agent TRMM Install date_descending v1.5",
"download_url": "https://raw.githubusercontent.com/amidaware/reporting-templates/master/templates/Agent%20TRMM%20Install%20date_descending%20v1.5.json"
},
{
"name": "Agent Uptime_By Client (html) v1.5",
Expand Down Expand Up @@ -148,8 +152,12 @@
"download_url": "https://raw.githubusercontent.com/amidaware/reporting-templates/master/templates/Antivirus%20Report.json"
},
{
"name": "Antivirus Report v1.1",
"download_url": "https://raw.githubusercontent.com/amidaware/reporting-templates/master/templates/Antivirus%20Report%20v1.1.json"
"name": "Antivirus Report v1.3",
"download_url": "https://raw.githubusercontent.com/amidaware/reporting-templates/master/templates/Antivirus%20Report%20v1.3.json"
},
{
"name": "Antivirus Report_by Client v1.3",
"download_url": "https://raw.githubusercontent.com/amidaware/reporting-templates/master/templates/Antivirus%20Report_by%20Client%20v1.3.json"
},
{
"name": "Audit Logs (csv)",
Expand Down
17 changes: 17 additions & 0 deletions templates/Agent TRMM Install date_by Client descending v1.5.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"base_template": {
"name": "TRMM_Base v1",
"html": "<html>\n\n<head>\n <style>\n /* —— PDF page settings —— */\n @page {\n margin: 0.5in;\n }\n\n /* —— Color palette (theme) —— */\n :root {\n --header-bg: #2c3e50;\n --header-text: #ffffff;\n --border-color: #dddddd;\n --table-header-bg: #f2f2f2;\n --status-ready-bg: #eaf7ec;\n --status-ready-badge: #28a745;\n --status-not-ready-bg: #fbebee;\n --status-not-ready-badge: #dc3545;\n --status-unknown-bg: #fff9e6;\n --status-unknown-badge: #ffc107;\n --badge-already-bg: #17a2b8;\n }\n\n /* —— Typography & utilities (structure) —— */\n body {\n font-family: sans-serif;\n color: #333;\n }\n\n .section {\n margin-top: 20px;\n }\n\n .text-danger {\n color: var(--status-not-ready-badge);\n font-weight: bold;\n }\n\n /* —— Header layout (structure) —— */\n .report-header {\n background-color: var(--header-bg);\n color: var(--header-text);\n padding: 20px;\n border-radius: 8px;\n display: flex;\n align-items: center;\n }\n\n .header-logo {\n width: 80px;\n margin-right: 25px;\n flex-shrink: 0;\n }\n\n .header-date {\n margin-left: auto;\n text-align: right;\n white-space: nowrap;\n }\n\n .report-header h1 {\n font-size: 24px;\n margin: 0 0 5px;\n }\n\n .report-header h2 {\n font-size: 16px;\n margin: 0;\n opacity: 0.9;\n }\n\n /* —— Table basics (structure) —— */\n .report-table {\n border-collapse: collapse;\n width: 100%;\n font-size: 12px;\n }\n\n .report-table th,\n .report-table td {\n border: 1px solid var(--border-color);\n padding: 8px 12px;\n text-align: left;\n vertical-align: middle;\n }\n\n .report-table thead th {\n background-color: var(--table-header-bg);\n font-weight: bold;\n }\n\n /* —— Badge shape (structure) —— */\n .status-badge {\n display: inline-block;\n padding: 4px 10px;\n border-radius: 12px;\n color: #fff;\n font-weight: bold;\n font-size: 11px;\n text-align: center;\n white-space: nowrap;\n }\n\n /* —— Stub classes for per‑report overrides —— */\n .status-ready {}\n\n .status-not-ready {}\n\n .status-unknown {}\n\n .badge-ready {}\n\n .badge-not-ready {}\n\n .badge-unknown {}\n\n .badge-already {}\n </style>\n</head>\n\n<body>\n {% block content %}{% endblock %}\n</body>\n\n</html>"
},
"template": {
"name": "Agent TRMM Install date_by Client descending v1.5",
"template_css": "",
"template_md": "{% block content %}\r\n<style>\r\n @page { margin: 0.5in; size: letter landscape; }\r\n\r\n /* Filter dropdowns inside headers */\r\n .report-table thead th select{\r\n width:100%; margin-top:5px; padding:4px; border-radius:4px; border:1px solid #ccc; font-weight:normal;\r\n background:#fff;\r\n }\r\n\r\n /* Date column width */\r\n .report-table th:nth-child(5), .report-table td:nth-child(5){ width:150px; }\r\n\r\n /* Header label row + indicators */\r\n .th-topline{ display:flex; align-items:center; gap:.4rem; }\r\n .th-label{ position:relative; }\r\n /* indicator beside the name via CSS, driven by classes on TH */\r\n th.sorted.asc .th-label::after{ content:\"▲\"; margin-left:.25rem; color:#666; font-size:.9em; }\r\n th.sorted.desc .th-label::after{ content:\"▼\"; margin-left:.25rem; color:#666; font-size:.9em; }\r\n</style>\r\n\r\n<div class=\"report-header\">\r\n <div class=\"header-logo\">\r\n <img src=\"https://github.com/amidaware/reporting-templates/blob/master/assets/gbtnavy%20(256).png?raw=true\" alt=\"Amidaware Logo\" style=\"width:80px;height:auto;\">\r\n </div>\r\n <div class=\"header-titles\">\r\n <h1>TRMM Agents Installation Report</h1>\r\n <h2 id=\"report-subtitle\">Client: All | Site: All</h2>\r\n </div>\r\n <div class=\"header-date\">\r\n <h2>Report Date:<br>{{ report_run_timestamp.strftime('%B %d, %Y') }}</h2>\r\n </div>\r\n</div>\r\n\r\n{% set clients = [] %}\r\n{% set sites = [] %}\r\n{% for item in data_sources.agents %}\r\n {% if item.site__client__name not in clients %}{% set _ = clients.append(item.site__client__name) %}{% endif %}\r\n {% if item.site__name not in sites %}{% set _ = sites.append(item.site__name) %}{% endif %}\r\n{% endfor %}\r\n\r\n<div class=\"section\">\r\n <table id=\"reportTable\" class=\"report-table\">\r\n <thead>\r\n <tr>\r\n <th data-col=\"0\">\r\n <div class=\"th-topline\">\r\n <span class=\"th-label\">Client</span>\r\n </div>\r\n <select id=\"client-filter\">\r\n <option value=\"all\">All Clients</option>\r\n {% for client in clients | sort %}\r\n <option value=\"{{ client }}\">{{ client }}</option>\r\n {% endfor %}\r\n </select>\r\n </th>\r\n <th data-col=\"1\">\r\n <div class=\"th-topline\">\r\n <span class=\"th-label\">Site</span>\r\n </div>\r\n <select id=\"site-filter\">\r\n <option value=\"all\">All Sites</option>\r\n {% for site in sites | sort %}\r\n <option value=\"{{ site }}\">{{ site }}</option>\r\n {% endfor %}\r\n </select>\r\n </th>\r\n <th data-col=\"2\">\r\n <div class=\"th-topline\"><span class=\"th-label\">Agent</span></div>\r\n </th>\r\n <th data-col=\"3\">\r\n <div class=\"th-topline\"><span class=\"th-label\">User</span></div>\r\n </th>\r\n <th data-col=\"4\">\r\n <div class=\"th-topline\"><span class=\"th-label\">Installation Date</span></div>\r\n </th>\r\n </tr>\r\n </thead>\r\n <tbody id=\"report-table-body\">\r\n {% for item in data_sources.agents %}\r\n <tr>\r\n <td>{{ item.site__client__name }}</td>\r\n <td>{{ item.site__name }}</td>\r\n <td>{{ item.hostname }}</td>\r\n <td>{{ item.last_logged_in_user }}</td>\r\n <td data-sort-value=\"{% if item.created_time %}{{ item.created_time.isoformat() }}{% else %}N/A{% endif %}\">\r\n {% if item.created_time %}{{ item.created_time.strftime('%Y-%m-%d %H:%M') }}{% else %}N/A{% endif %}\r\n </td>\r\n </tr>\r\n {% endfor %}\r\n </tbody>\r\n </table>\r\n</div>\r\n\r\n<script>\r\ndocument.addEventListener(\"DOMContentLoaded\", () => {\r\n const clientFilter = document.getElementById(\"client-filter\");\r\n const siteFilter = document.getElementById(\"site-filter\");\r\n const tableBody = document.getElementById(\"report-table-body\");\r\n const reportSubtitle = document.getElementById(\"report-subtitle\");\r\n const headers = Array.from(document.querySelectorAll(\"#reportTable thead th\"));\r\n\r\n let currentSortColumn = null;\r\n let currentSortDirection = \"asc\";\r\n\r\n function updateIndicators() {\r\n headers.forEach(h => h.classList.remove(\"sorted\",\"asc\",\"desc\"));\r\n if (currentSortColumn !== null) {\r\n const th = headers[currentSortColumn];\r\n th.classList.add(\"sorted\", currentSortDirection);\r\n th.setAttribute(\"aria-sort\", currentSortDirection);\r\n } else {\r\n headers.forEach(h => h.removeAttribute(\"aria-sort\"));\r\n }\r\n }\r\n\r\n function applyFilters() {\r\n const selClient = clientFilter.value;\r\n const selSite = siteFilter.value;\r\n\r\n reportSubtitle.textContent =\r\n `Client: ${selClient === 'all' ? 'All' : selClient} | Site: ${selSite === 'all' ? 'All' : selSite}`;\r\n\r\n const rows = Array.from(tableBody.rows);\r\n rows.forEach(row => {\r\n const client = row.cells[0].textContent.trim();\r\n const site = row.cells[1].textContent.trim();\r\n const show = (selClient === \"all\" || client === selClient) &&\r\n (selSite === \"all\" || site === selSite);\r\n row.style.display = show ? \"\" : \"none\";\r\n });\r\n\r\n if (currentSortColumn !== null) sortTable(currentSortColumn, currentSortDirection, false);\r\n }\r\n\r\n function sortTable(colIndex, direction, toggle = true) {\r\n const rows = Array.from(tableBody.rows);\r\n const visible = rows.filter(r => r.style.display !== \"none\");\r\n\r\n if (toggle) {\r\n if (currentSortColumn === colIndex) {\r\n currentSortDirection = currentSortDirection === \"asc\" ? \"desc\" : \"asc\";\r\n } else {\r\n currentSortDirection = \"asc\";\r\n }\r\n }\r\n currentSortColumn = colIndex;\r\n\r\n visible.sort((a,b) => {\r\n let aVal, bVal;\r\n if (colIndex === 4) {\r\n aVal = a.cells[colIndex].getAttribute(\"data-sort-value\") || \"\";\r\n bVal = b.cells[colIndex].getAttribute(\"data-sort-value\") || \"\";\r\n const da = new Date(aVal), db = new Date(bVal);\r\n if (isNaN(da) && isNaN(db)) return 0;\r\n if (isNaN(da)) return 1;\r\n if (isNaN(db)) return -1;\r\n return da - db;\r\n } else {\r\n aVal = a.cells[colIndex].textContent.trim();\r\n bVal = b.cells[colIndex].textContent.trim();\r\n return aVal.localeCompare(bVal, undefined, { numeric:true, sensitivity:\"base\" });\r\n }\r\n });\r\n\r\n if (currentSortDirection === \"desc\") visible.reverse();\r\n\r\n tableBody.innerHTML = \"\";\r\n visible.forEach(r => tableBody.appendChild(r));\r\n rows.filter(r => r.style.display === \"none\").forEach(r => tableBody.appendChild(r));\r\n\r\n updateIndicators();\r\n }\r\n\r\n // Click sort on label only; selects won’t trigger sort\r\n headers.forEach((th, idx) => {\r\n const label = th.querySelector(\".th-label\");\r\n if (label) {\r\n label.style.cursor = \"pointer\";\r\n label.addEventListener(\"click\", () => sortTable(idx, currentSortDirection));\r\n }\r\n });\r\n\r\n clientFilter.addEventListener(\"change\", applyFilters);\r\n siteFilter.addEventListener(\"change\", applyFilters);\r\n\r\n // Initial render; no default sort so no indicator until user clicks\r\n applyFilters();\r\n});\r\n</script>\r\n{% endblock %}",
"type": "html",
"depends_on": [
"client"
],
"template_variables": "data_sources:\r\n agents:\r\n model: agent\r\n select_related:\r\n - site\r\n - site__client\r\n only:\r\n - hostname\r\n - last_logged_in_user\r\n - created_time\r\n - site__name\r\n - site__client__name\r\n order_by:\r\n - -created_time\r\n filter:\r\n site__client_id: \"{{client.id}}\"\r\nreport_run_timestamp: !now"
},
"assets": []
}
Loading