CoolFace
Apppublic

Mr-vabs/itsam-tool-normalization-engine-31l34

sourceHugging Faceupdated 7mo agoView on Hugging Face
0likes
index.html1199 linesDownload Raw Back to root
1<!DOCTYPE html>2<html lang="en">3<head>4    <meta charset="UTF-8">5    <meta name="viewport" content="width=device-width, initial-scale=1.0">6    <title>ITSAM Tool - Normalization Engine</title>7    <script src="https://cdn.tailwindcss.com"></script>8    <script src="https://unpkg.com/lucide@latest"></script>9    <script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js"></script>10    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">11    <style>12        * { font-family: 'Inter', sans-serif; }13        .drop-zone { transition: all 0.3s ease; }14        .drop-zone.dragover { border-color: #3b82f6; background: #eff6ff; transform: scale(1.02); }15        .data-table th { position: sticky; top: 0; background: #f8fafc; z-index: 10; }16        .data-table { font-size: 0.85rem; }17        .data-table th, .data-table td { padding: 0.5rem 0.75rem; white-space: nowrap; }18        .tab-active { border-bottom: 2px solid #3b82f6; color: #3b82f6; }19        .modal { backdrop-filter: blur(4px); }20        .scrollbar-thin::-webkit-scrollbar { width: 6px; height: 6px; }21        .scrollbar-thin::-webkit-scrollbar-track { background: #f1f5f9; }22        .scrollbar-thin::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 3px; }23        .badge { padding: 2px 8px; border-radius: 12px; font-size: 0.75rem; font-weight: 500; }24        .badge-freeware { background: #dcfce7; color: #166534; }25        .badge-standard { background: #dbeafe; color: #1e40af; }26        .badge-blacklisted { background: #fee2e2; color: #991b1b; }27        .badge-unknown { background: #f1f5f9; color: #64748b; }28    </style>29</head>30<body class="bg-slate-50 min-h-screen">31    <!-- Header -->32    <header class="bg-white border-b border-slate-200 shadow-sm">33        <div class="max-w-7xl mx-auto px-4 py-4 flex items-center justify-between">34            <div class="flex items-center gap-3">35                <div class="w-10 h-10 bg-gradient-to-br from-blue-600 to-indigo-700 rounded-lg flex items-center justify-center">36                    <i data-lucide="database" class="text-white w-5 h-5"></i>37                </div>38                <div>39                    <h1 class="text-xl font-bold text-slate-800">ITSAM Tool</h1>40                    <p class="text-xs text-slate-500">Normalization Engine</p>41                </div>42            </div>43            <div class="flex items-center gap-4">44                <button onclick="resetAllData()" class="px-4 py-2 text-sm text-slate-600 hover:text-red-600 hover:bg-red-50 rounded-lg transition flex items-center gap-2">45                    <i data-lucide="refresh-cw" class="w-4 h-4"></i>46                    Reset47                </button>48                <button onclick="exportAllData()" class="px-4 py-2 bg-blue-600 text-white text-sm rounded-lg hover:bg-blue-700 transition flex items-center gap-2">49                    <i data-lucide="download" class="w-4 h-4"></i>50                    Export All51                </button>52            </div>53        </div>54    </header>55 56    <!-- Main Content -->57    <main class="max-w-7xl mx-auto px-4 py-6">58        <!-- Tabs -->59        <div class="bg-white rounded-t-xl border border-slate-200 border-b-0">60            <div class="flex overflow-x-auto">61                <button onclick="switchTab('upload')" id="tab-upload" class="tab-btn tab-active px-6 py-3 text-sm font-medium text-slate-600 hover:text-blue-600 transition">62                    <i data-lucide="upload-cloud" class="w-4 h-4 inline mr-2"></i>Upload Data63                </button>64                <button onclick="switchTab('dashboard')" id="tab-dashboard" class="tab-btn px-6 py-3 text-sm font-medium text-slate-600 hover:text-blue-600 transition">65                    <i data-lucide="layout-dashboard" class="w-4 h-4 inline mr-2"></i>Dashboard66                </button>67                <button onclick="switchTab('normalized')" id="tab-normalized" class="tab-btn px-6 py-3 text-sm font-medium text-slate-600 hover:text-blue-600 transition">68                    <i data-lucide="link" class="w-4 h-4 inline mr-2"></i>Normalized Data69                </button>70                <button onclick="switchTab('relations')" id="tab-relations" class="tab-btn px-6 py-3 text-sm font-medium text-slate-600 hover:text-blue-600 transition">71                    <i data-lucide="network" class="w-4 h-4 inline mr-2"></i>Relational Views72                </button>73                <button onclick="switchTab('sr')" id="tab-sr" class="tab-btn px-6 py-3 text-sm font-medium text-slate-600 hover:text-blue-600 transition">74                    <i data-lucide="file-spreadsheet" class="w-4 h-4 inline mr-2"></i>SR Expansion75                </button>76            </div>77        </div>78 79        <!-- Upload Tab -->80        <div id="view-upload" class="view-section">81            <div class="bg-white border border-slate-200 rounded-b-xl p-6">82                <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 mb-6">83                    <!-- Upload Zones -->84                    <div class="space-y-2">85                        <h3 class="text-sm font-semibold text-slate-700 flex items-center gap-2">86                            <i data-lucide="monitor" class="w-4 h-4 text-blue-500"></i>SCCM Discovery87                        </h3>88                        <div class="drop-zone border-2 border-dashed border-slate-300 rounded-lg p-4 text-center cursor-pointer hover:border-blue-400" 89                             data-type="sccm" ondrop="handleDrop(event, 'sccm')" ondragover="handleDragOver(event)" ondragleave="handleDragLeave(event)" onclick="triggerFileInput('sccm')">90                            <input type="file" id="file-sccm" class="hidden" accept=".csv,.xlsx,.xls" onchange="handleFileSelect(event, 'sccm')">91                            <i data-lucide="file-spreadsheet" class="w-8 h-8 mx-auto text-slate-400 mb-2"></i>92                            <p class="text-sm text-slate-600">Drop CSV/Excel here</p>93                            <p id="status-sccm" class="text-xs text-green-600 mt-1 hidden"></p>94                        </div>95                    </div>96                    <div class="space-y-2">97                        <h3 class="text-sm font-semibold text-slate-700 flex items-center gap-2">98                            <i data-lucide="hard-drive" class="w-4 h-4 text-green-500"></i>Asset Owner99                        </h3>100                        <div class="drop-zone border-2 border-dashed border-slate-300 rounded-lg p-4 text-center cursor-pointer hover:border-green-400" 101                             data-type="asset" ondrop="handleDrop(event, 'asset')" ondragover="handleDragOver(event)" ondragleave="handleDragLeave(event)" onclick="triggerFileInput('asset')">102                            <input type="file" id="file-asset" class="hidden" accept=".csv,.xlsx,.xls" onchange="handleFileSelect(event, 'asset')">103                            <i data-lucide="file-spreadsheet" class="w-8 h-8 mx-auto text-slate-400 mb-2"></i>104                            <p class="text-sm text-slate-600">Drop CSV/Excel here</p>105                            <p id="status-asset" class="text-xs text-green-600 mt-1 hidden"></p>106                        </div>107                    </div>108                    <div class="space-y-2">109                        <h3 class="text-sm font-semibold text-slate-700 flex items-center gap-2">110                            <i data-lucide="users" class="w-4 h-4 text-purple-500"></i>User Master111                        </h3>112                        <div class="drop-zone border-2 border-dashed border-slate-300 rounded-lg p-4 text-center cursor-pointer hover:border-purple-400" 113                             data-type="user" ondrop="handleDrop(event, 'user')" ondragover="handleDragOver(event)" ondragleave="handleDragLeave(event)" onclick="triggerFileInput('user')">114                            <input type="file" id="file-user" class="hidden" accept=".csv,.xlsx,.xls" onchange="handleFileSelect(event, 'user')">115                            <i data-lucide="file-spreadsheet" class="w-8 h-8 mx-auto text-slate-400 mb-2"></i>116                            <p class="text-sm text-slate-600">Drop CSV/Excel here</p>117                            <p id="status-user" class="text-xs text-green-600 mt-1 hidden"></p>118                        </div>119                    </div>120                    <div class="space-y-2">121                        <h3 class="text-sm font-semibold text-slate-700 flex items-center gap-2">122                            <i data-lucide="package" class="w-4 h-4 text-orange-500"></i>Master Software List123                        </h3>124                        <div class="drop-zone border-2 border-dashed border-slate-300 rounded-lg p-4 text-center cursor-pointer hover:border-orange-400" 125                             data-type="software" ondrop="handleDrop(event, 'software')" ondragover="handleDragOver(event)" ondragleave="handleDragLeave(event)" onclick="triggerFileInput('software')">126                            <input type="file" id="file-software" class="hidden" accept=".csv,.xlsx,.xls" onchange="handleFileSelect(event, 'software')">127                            <i data-lucide="file-spreadsheet" class="w-8 h-8 mx-auto text-slate-400 mb-2"></i>128                            <p class="text-sm text-slate-600">Drop CSV/Excel here</p>129                            <p id="status-software" class="text-xs text-green-600 mt-1 hidden"></p>130                        </div>131                    </div>132                    <div class="space-y-2">133                        <h3 class="text-sm font-semibold text-slate-700 flex items-center gap-2">134                            <i data-lucide="clipboard-check" class="w-4 h-4 text-red-500"></i>Approved SR Data135                        </h3>136                        <div class="drop-zone border-2 border-dashed border-slate-300 rounded-lg p-4 text-center cursor-pointer hover:border-red-400" 137                             data-type="sr" ondrop="handleDrop(event, 'sr')" ondragover="handleDragOver(event)" ondragleave="handleDragLeave(event)" onclick="triggerFileInput('sr')">138                            <input type="file" id="file-sr" class="hidden" accept=".csv,.xlsx,.xls" onchange="handleFileSelect(event, 'sr')">139                            <i data-lucide="file-spreadsheet" class="w-8 h-8 mx-auto text-slate-400 mb-2"></i>140                            <p class="text-sm text-slate-600">Drop CSV/Excel here</p>141                            <p id="status-sr" class="text-xs text-green-600 mt-1 hidden"></p>142                        </div>143                    </div>144                </div>145 146                <button onclick="processAllData()" class="w-full py-3 bg-gradient-to-r from-blue-600 to-indigo-600 text-white font-semibold rounded-lg hover:from-blue-700 hover:to-indigo-700 transition flex items-center justify-center gap-2">147                    <i data-lucide="zap" class="w-5 h-5"></i>148                    Process & Normalize Data149                </button>150            </div>151        </div>152 153        <!-- Dashboard Tab -->154        <div id="view-dashboard" class="view-section hidden">155            <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-6">156                <!-- Stats Cards -->157                <div class="bg-white rounded-xl border border-slate-200 p-5 shadow-sm">158                    <div class="flex items-center justify-between mb-3">159                        <span class="text-sm text-slate-500">SCCM Records</span>160                        <div class="w-8 h-8 bg-blue-100 rounded-lg flex items-center justify-center">161                            <i data-lucide="monitor" class="w-4 h-4 text-blue-600"></i>162                        </div>163                    </div>164                    <p id="stat-sccm" class="text-2xl font-bold text-slate-800">0</p>165                </div>166                <div class="bg-white rounded-xl border border-slate-200 p-5 shadow-sm">167                    <div class="flex items-center justify-between mb-3">168                        <span class="text-sm text-slate-500">Total Assets</span>169                        <div class="w-8 h-8 bg-green-100 rounded-lg flex items-center justify-center">170                            <i data-lucide="hard-drive" class="w-4 h-4 text-green-600"></i>171                        </div>172                    </div>173                    <p id="stat-assets" class="text-2xl font-bold text-slate-800">0</p>174                </div>175                <div class="bg-white rounded-xl border border-slate-200 p-5 shadow-sm">176                    <div class="flex items-center justify-between mb-3">177                        <span class="text-sm text-slate-500">Users</span>178                        <div class="w-8 h-8 bg-purple-100 rounded-lg flex items-center justify-center">179                            <i data-lucide="users" class="w-4 h-4 text-purple-600"></i>180                        </div>181                    </div>182                    <p id="stat-users" class="text-2xl font-bold text-slate-800">0</p>183                </div>184                <div class="bg-white rounded-xl border border-slate-200 p-5 shadow-sm">185                    <div class="flex items-center justify-between mb-3">186                        <span class="text-sm text-slate-500">Normalization Rate</span>187                        <div class="w-8 h-8 bg-orange-100 rounded-lg flex items-center justify-center">188                            <i data-lucide="percent" class="w-4 h-4 text-orange-600"></i>189                        </div>190                    </div>191                    <p id="stat-norm-rate" class="text-2xl font-bold text-slate-800">0%</p>192                </div>193            </div>194 195            <!-- Warning Cards -->196            <div class="grid grid-cols-1 lg:grid-cols-2 gap-4 mb-6">197                <div class="bg-white rounded-xl border border-slate-200 p-5 shadow-sm">198                    <h3 class="text-sm font-semibold text-slate-700 mb-3 flex items-center gap-2">199                        <i data-lucide="alert-triangle" class="w-4 h-4 text-red-500"></i>200                        Blacklisted Software Found201                    </h3>202                    <div id="blacklisted-list" class="space-y-2 max-h-48 overflow-y-auto scrollbar-thin">203                        <p class="text-sm text-slate-400 italic">No blacklisted software detected</p>204                    </div>205                </div>206                <div class="bg-white rounded-xl border border-slate-200 p-5 shadow-sm">207                    <h3 class="text-sm font-semibold text-slate-700 mb-3 flex items-center gap-2">208                        <i data-lucide="check-circle" class="w-4 h-4 text-green-500"></i>209                        Normalization Summary210                    </h3>211                    <div id="norm-summary" class="space-y-2 max-h-48 overflow-y-auto scrollbar-thin">212                        <p class="text-sm text-slate-400 italic">Upload data to see summary</p>213                    </div>214                </div>215            </div>216 217            <!-- Upload Status -->218            <div class="bg-white rounded-xl border border-slate-200 p-5 shadow-sm">219                <h3 class="text-sm font-semibold text-slate-700 mb-3 flex items-center gap-2">220                    <i data-lucide="folder-open" class="w-4 h-4 text-blue-500"></i>221                    Uploaded Data Files222                </h3>223                <div class="grid grid-cols-2 md:grid-cols-5 gap-3" id="upload-status">224                    <div class="p-3 bg-slate-50 rounded-lg text-center">225                        <p class="text-xs text-slate-500">SCCM</p>226                        <p id="upload-sccm" class="text-sm font-medium text-slate-400">Pending</p>227                    </div>228                    <div class="p-3 bg-slate-50 rounded-lg text-center">229                        <p class="text-xs text-slate-500">Assets</p>230                        <p id="upload-asset" class="text-sm font-medium text-slate-400">Pending</p>231                    </div>232                    <div class="p-3 bg-slate-50 rounded-lg text-center">233                        <p class="text-xs text-slate-500">Users</p>234                        <p id="upload-user" class="text-sm font-medium text-slate-400">Pending</p>235                    </div>236                    <div class="p-3 bg-slate-50 rounded-lg text-center">237                        <p class="text-xs text-slate-500">Software</p>238                        <p id="upload-software" class="text-sm font-medium text-slate-400">Pending</p>239                    </div>240                    <div class="p-3 bg-slate-50 rounded-lg text-center">241                        <p class="text-xs text-slate-500">SR Data</p>242                        <p id="upload-sr" class="text-sm font-medium text-slate-400">Pending</p>243                    </div>244                </div>245            </div>246        </div>247 248        <!-- Normalized Data Tab -->249        <div id="view-normalized" class="view-section hidden">250            <div class="bg-white border border-slate-200 rounded-xl">251                <!-- Toolbar -->252                <div class="p-4 border-b border-slate-200 flex flex-wrap items-center gap-3">253                    <div class="relative flex-1 min-w-64">254                        <i data-lucide="search" class="w-4 h-4 absolute left-3 top-1/2 -translate-y-1/2 text-slate-400"></i>255                        <input type="text" id="search-normalized" placeholder="Search normalized data..." 256                               class="w-full pl-10 pr-4 py-2 border border-slate-300 rounded-lg text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500"257                               oninput="filterNormalizedData()">258                    </div>259                    <select id="filter-license" class="px-3 py-2 border border-slate-300 rounded-lg text-sm focus:ring-2 focus:ring-blue-500"260                            onchange="filterNormalizedData()">261                        <option value="">All License Types</option>262                        <option value="Freeware">Freeware</option>263                        <option value="License Standard">License Standard</option>264                        <option value="Blacklisted">Blacklisted</option>265                        <option value="Unknown">Unknown</option>266                    </select>267                    <button onclick="exportData('normalized')" class="px-4 py-2 bg-green-600 text-white text-sm rounded-lg hover:bg-green-700 transition flex items-center gap-2">268                        <i data-lucide="download" class="w-4 h-4"></i>Export269                    </button>270                </div>271                <!-- Table -->272                <div class="overflow-auto max-h-[600px] scrollbar-thin">273                    <table class="data-table w-full border-collapse">274                        <thead class="bg-slate-50">275                            <tr>276                                <th class="text-left cursor-pointer hover:bg-slate-100" onclick="sortTable('normalized', 'computerName')">277                                    <span class="flex items-center gap-1">Computer <i data-lucide="arrow-up-down" class="w-3 h-3"></i></span>278                                </th>279                                <th class="text-left cursor-pointer hover:bg-slate-100" onclick="sortTable('normalized', 'rawSoftware')">280                                    <span class="flex items-center gap-1">Raw Software <i data-lucide="arrow-up-down" class="w-3 h-3"></i></span>281                                </th>282                                <th class="text-left cursor-pointer hover:bg-slate-100" onclick="sortTable('normalized', 'standardTitle')">283                                    <span class="flex items-center gap-1">Standard Title <i data-lucide="arrow-up-down" class="w-3 h-3"></i></span>284                                </th>285                                <th class="text-left cursor-pointer hover:bg-slate-100" onclick="sortTable('normalized', 'publisher')">286                                    <span class="flex items-center gap-1">Publisher <i data-lucide="arrow-up-down" class="w-3 h-3"></i></span>287                                </th>288                                <th class="text-left cursor-pointer hover:bg-slate-100" onclick="sortTable('normalized', 'version')">289                                    <span class="flex items-center gap-1">Version <i data-lucide="arrow-up-down" class="w-3 h-3"></i></span>290                                </th>291                                <th class="text-left cursor-pointer hover:bg-slate-100" onclick="sortTable('normalized', 'licenseType')">292                                    <span class="flex items-center gap-1">License Type <i data-lucide="arrow-up-down" class="w-3 h-3"></i></span>293                                </th>294                                <th class="text-left">Status</th>295                            </tr>296                        </thead>297                        <tbody id="normalized-table-body">298                            <tr><td colspan="7" class="text-center py-8 text-slate-400">Upload and process data to see normalized results</td></tr>299                        </tbody>300                    </table>301                </div>302                <div class="p-3 border-t border-slate-200 text-sm text-slate-500">303                    <span id="normalized-count">0</span> records304                </div>305            </div>306        </div>307 308        <!-- Relational Views Tab -->309        <div id="view-relations" class="view-section hidden">310            <div class="bg-white border border-slate-200 rounded-xl">311                <div class="p-4 border-b border-slate-200">312                    <h3 class="text-lg font-semibold text-slate-800 mb-3">Relational Data Views</h3>313                    <div class="flex gap-2 flex-wrap">314                        <button onclick="switchRelationView('software-per-user')" class="relation-btn px-4 py-2 text-sm rounded-lg bg-blue-100 text-blue-700 font-medium" data-view="software-per-user">315                            <i data-lucide="user" class="w-4 h-4 inline mr-1"></i>Software per User316                        </button>317                        <button onclick="switchRelationView('software-per-asset')" class="relation-btn px-4 py-2 text-sm rounded-lg bg-slate-100 text-slate-600" data-view="software-per-asset">318                            <i data-lucide="hard-drive" class="w-4 h-4 inline mr-1"></i>Software per Asset319                        </button>320                        <button onclick="switchRelationView('user-asset-link')" class="relation-btn px-4 py-2 text-sm rounded-lg bg-slate-100 text-slate-600" data-view="user-asset-link">321                            <i data-lucide="link" class="w-4 h-4 inline mr-1"></i>User-Asset Links322                        </button>323                    </div>324                </div>325                326                <!-- Software per User View -->327                <div id="view-software-per-user" class="relation-view">328                    <div class="p-4 border-b border-slate-200 flex flex-wrap items-center gap-3">329                        <div class="relative flex-1 min-w-64">330                            <i data-lucide="search" class="w-4 h-4 absolute left-3 top-1/2 -translate-y-1/2 text-slate-400"></i>331                            <input type="text" id="search-relations-user" placeholder="Search by user or software..." 332                                   class="w-full pl-10 pr-4 py-2 border border-slate-300 rounded-lg text-sm"333                                   oninput="filterRelationData('software-per-user')">334                        </div>335                        <button onclick="exportData('software-per-user')" class="px-4 py-2 bg-green-600 text-white text-sm rounded-lg hover:bg-green-700 transition flex items-center gap-2">336                            <i data-lucide="download" class="w-4 h-4"></i>Export337                        </button>338                    </div>339                    <div class="overflow-auto max-h-[500px] scrollbar-thin">340                        <table class="data-table w-full border-collapse">341                            <thead class="bg-slate-50">342                                <tr>343                                    <th class="text-left">User ID</th>344                                    <th class="text-left">Full Name</th>345                                    <th class="text-left">Department</th>346                                    <th class="text-left">Email</th>347                                    <th class="text-left">Software Title</th>348                                    <th class="text-left">Standard Title</th>349                                    <th class="text-left">License Type</th>350                                </tr>351                            </thead>352                            <tbody id="software-per-user-body">353                                <tr><td colspan="7" class="text-center py-8 text-slate-400">Process data to see relations</td></tr>354                            </tbody>355                        </table>356                    </div>357                </div>358 359                <!-- Software per Asset View -->360                <div id="view-software-per-asset" class="relation-view hidden">361                    <div class="p-4 border-b border-slate-200 flex flex-wrap items-center gap-3">362                        <div class="relative flex-1 min-w-64">363                            <i data-lucide="search" class="w-4 h-4 absolute left-3 top-1/2 -translate-y-1/2 text-slate-400"></i>364                            <input type="text" id="search-relations-asset" placeholder="Search by asset or software..." 365                                   class="w-full pl-10 pr-4 py-2 border border-slate-300 rounded-lg text-sm"366                                   oninput="filterRelationData('software-per-asset')">367                        </div>368                        <button onclick="exportData('software-per-asset')" class="px-4 py-2 bg-green-600 text-white text-sm rounded-lg hover:bg-green-700 transition flex items-center gap-2">369                            <i data-lucide="download" class="w-4 h-4"></i>Export370                        </button>371                    </div>372                    <div class="overflow-auto max-h-[500px] scrollbar-thin">373                        <table class="data-table w-full border-collapse">374                            <thead class="bg-slate-50">375                                <tr>376                                    <th class="text-left">Asset ID</th>377                                    <th class="text-left">Serial Number</th>378                                    <th class="text-left">Computer Name</th>379                                    <th class="text-left">Location</th>380                                    <th class="text-left">Software Title</th>381                                    <th class="text-left">Standard Title</th>382                                    <th class="text-left">Version</th>383                                    <th class="text-left">License Type</th>384                                </tr>385                            </thead>386                            <tbody id="software-per-asset-body">387                                <tr><td colspan="8" class="text-center py-8 text-slate-400">Process data to see relations</td></tr>388                            </tbody>389                        </table>390                    </div>391                </div>392 393                <!-- User-Asset Link View -->394                <div id="view-user-asset-link" class="relation-view hidden">395                    <div class="p-4 border-b border-slate-200 flex flex-wrap items-center gap-3">396                        <div class="relative flex-1 min-w-64">397                            <i data-lucide="search" class="w-4 h-4 absolute left-3 top-1/2 -translate-y-1/2 text-slate-400"></i>398                            <input type="text" id="search-relations-link" placeholder="Search links..." 399                                   class="w-full pl-10 pr-4 py-2 border border-slate-300 rounded-lg text-sm"400                                   oninput="filterRelationData('user-asset-link')">401                        </div>402                        <button onclick="exportData('user-asset-link')" class="px-4 py-2 bg-green-600 text-white text-sm rounded-lg hover:bg-green-700 transition flex items-center gap-2">403                            <i data-lucide="download" class="w-4 h-4"></i>Export404                        </button>405                    </div>406                    <div class="overflow-auto max-h-[500px] scrollbar-thin">407                        <table class="data-table w-full border-collapse">408                            <thead class="bg-slate-50">409                                <tr>410                                    <th class="text-left">User ID</th>411                                    <th class="text-left">Full Name</th>412                                    <th class="text-left">Department</th>413                                    <th class="text-left">Asset ID</th>414                                    <th class="text-left">Serial Number</th>415                                    <th class="text-left">Computer Name</th>416                                    <th class="text-left">Location</th>417                                    <th class="text-left">Asset Status</th>418                                </tr>419                            </thead>420                            <tbody id="user-asset-link-body">421                                <tr><td colspan="8" class="text-center py-8 text-slate-400">Process data to see relations</td></tr>422                            </tbody>423                        </table>424                    </div>425                </div>426            </div>427        </div>428 429        <!-- SR Expansion Tab -->430        <div id="view-sr" class="view-section hidden">431            <div class="bg-white border border-slate-200 rounded-xl">432                <div class="p-4 border-b border-slate-200">433                    <h3 class="text-lg font-semibold text-slate-800 mb-2">Service Request Expansion (Cartesian Product)</h3>434                    <p class="text-sm text-slate-500">Cross-join between Asset IDs and Approved Software in each SR</p>435                </div>436                <div class="p-4 border-b border-slate-200 flex flex-wrap items-center gap-3">437                    <div class="relative flex-1 min-w-64">438                        <i data-lucide="search" class="w-4 h-4 absolute left-3 top-1/2 -translate-y-1/2 text-slate-400"></i>439                        <input type="text" id="search-sr" placeholder="Search SR expansions..." 440                               class="w-full pl-10 pr-4 py-2 border border-slate-300 rounded-lg text-sm"441                               oninput="filterSRData()">442                    </div>443                    <button onclick="exportData('sr-expansion')" class="px-4 py-2 bg-green-600 text-white text-sm rounded-lg hover:bg-green-700 transition flex items-center gap-2">444                        <i data-lucide="download" class="w-4 h-4"></i>Export445                    </button>446                </div>447                <div class="overflow-auto max-h-[500px] scrollbar-thin">448                    <table class="data-table w-full border-collapse">449                        <thead class="bg-slate-50">450                            <tr>451                                <th class="text-left">SR Number</th>452                                <th class="text-left">Asset ID</th>453                                <th class="text-left">Approved Software</th>454                                <th class="text-left">Approval Date</th>455                                <th class="text-left">Approver Email</th>456                                <th class="text-left">Validity</th>457                            </tr>458                        </thead>459                        <tbody id="sr-expansion-body">460                            <tr><td colspan="6" class="text-center py-8 text-slate-400">Process data to see SR expansions</td></tr>461                        </tbody>462                    </table>463                </div>464                <div class="p-3 border-t border-slate-200 text-sm text-slate-500">465                    <span id="sr-expansion-count">0</span> expanded records (Cartesian product applied)466                </div>467            </div>468        </div>469    </main>470 471    <!-- Processing Modal -->472    <div id="processing-modal" class="modal fixed inset-0 bg-black/50 hidden flex items-center justify-center z-50">473        <div class="bg-white rounded-xl p-6 w-96 shadow-xl">474            <div class="flex items-center gap-3 mb-4">475                <div class="w-10 h-10 bg-blue-100 rounded-full flex items-center justify-center animate-pulse">476                    <i data-lucide="loader-2" class="w-5 h-5 text-blue-600"></i>477                </div>478                <div>479                    <h3 class="font-semibold text-slate-800">Processing Data</h3>480                    <p class="text-sm text-slate-500">Normalizing and linking...</p>481                </div>482            </div>483            <div class="w-full bg-slate-200 rounded-full h-2">484                <div id="processing-progress" class="bg-blue-600 h-2 rounded-full transition-all duration-300" style="width: 0%"></div>485            </div>486            <p id="processing-status" class="text-sm text-slate-500 mt-3 text-center">Starting...</p>487        </div>488    </div>489 490    <script>491        // Data Store492        let dataStore = {493            sccm: [],494            asset: [],495            user: [],496            software: [],497            sr: [],498            normalized: [],499            softwarePerUser: [],500            softwarePerAsset: [],501            userAssetLink: [],502            srExpansion: []503        };504 505        let sortState = { column: null, direction: 'asc' };506 507        // File Handling508        function handleDragOver(e) {509            e.preventDefault();510            e.currentTarget.classList.add('dragover');511        }512 513        function handleDragLeave(e) {514            e.currentTarget.classList.remove('dragover');515        }516 517        function handleDrop(e, type) {518            e.preventDefault();519            e.currentTarget.classList.remove('dragover');520            const files = e.dataTransfer.files;521            if (files.length > 0) {522                processFile(files[0], type);523            }524        }525 526        function triggerFileInput(type) {527            document.getElementById(`file-${type}`).click();528        }529 530        function handleFileSelect(e, type) {531            const file = e.target.files[0];532            if (file) {533                processFile(file, type);534            }535        }536 537        function processFile(file, type) {538            const reader = new FileReader();539            reader.onload = (e) => {540                const data = new Uint8Array(e.target.result);541                const workbook = XLSX.read(data, { type: 'array' });542                const firstSheet = workbook.Sheets[workbook.SheetNames[0]];543                const jsonData = XLSX.utils.sheet_to_json(firstSheet, { header: 1 });544                545                if (jsonData.length > 0) {546                    const headers = jsonData[0].map(h => h ? h.toString().trim() : '');547                    const rows = jsonData.slice(1).map(row => {548                        const obj = {};549                        headers.forEach((h, i) => obj[h] = row[i] ? row[i].toString().trim() : '');550                        return obj;551                    });552                    553                    dataStore[type] = rows;554                    document.getElementById(`status-${type}`).textContent = `${rows.length} records loaded`;555                    document.getElementById(`status-${type}`).classList.remove('hidden');556                    updateUploadStatus(type, rows.length);557                }558            };559            reader.readAsArrayBuffer(file);560        }561 562        function updateUploadStatus(type, count) {563            const labels = { sccm: 'SCCM', asset: 'Assets', user: 'Users', software: 'Software', sr: 'SR Data' };564            const el = document.getElementById(`upload-${type}`);565            el.textContent = count > 0 ? `${count} records` : 'Pending';566            el.className = count > 0 ? 'text-sm font-medium text-green-600' : 'text-sm font-medium text-slate-400';567        }568 569        // Data Processing570        async function processAllData() {571            if (dataStore.sccm.length === 0) {572                alert('Please upload at least the SCCM Discovery data');573                return;574            }575 576            const modal = document.getElementById('processing-modal');577            const progress = document.getElementById('processing-progress');578            const status = document.getElementById('processing-status');579            modal.classList.remove('hidden');580 581            // Simulate processing steps582            progress.style.width = '20%';583            status.textContent = 'Normalizing software names...';584            await sleep(300);585 586            normalizeData();587            progress.style.width = '50%';588            status.textContent = 'Building relations...';589            await sleep(300);590 591            buildRelations();592            progress.style.width = '75%';593            status.textContent = 'Expanding SR data...';594            await sleep(300);595 596            expandSRData();597            progress.style.width = '100%';598            status.textContent = 'Complete!';599            await sleep(500);600 601            modal.classList.add('hidden');602            updateDashboard();603            renderNormalizedTable();604            renderRelationTables();605            renderSRTable();606            switchTab('dashboard');607        }608 609        function normalizeData() {610            const softwareMap = {};611            dataStore.software.forEach(s => {612                const title = s['Software Title'] || s['Software Title '];613                const classification = s['Classification'] || s['Classification '];614                const label = s['Status_Label'] || s['Status_Label '] || s['Status Label'] || 'Unknown';615                if (title) {616                    softwareMap[title.toLowerCase()] = {617                        standardTitle: title,618                        classification: classification || 'Unclassified',619                        label: label620                    };621                }622            });623 624            // Also create fuzzy matching map625            Object.keys(softwareMap).forEach(key => {626                const words = key.split(/[\s\-\_]+/);627                words.forEach(word => {628                    if (word.length > 3 && !softwareMap[word]) {629                        softwareMap[word] = softwareMap[key];630                    }631                });632            });633 634            dataStore.normalized = dataStore.sccm.map(item => {635                const rawSoftware = item['Software Name'] || item['Software Name '] || '';636                const rawLower = rawSoftware.toLowerCase();637                638                let match = softwareMap[rawLower];639                640                // Fuzzy match641                if (!match) {642                    const words = rawLower.split(/[\s\-\_]+/);643                    for (let word of words) {644                        if (word.length > 3 && softwareMap[word]) {645                            match = softwareMap[word];646                            break;647                        }648                    }649                }650 651                return {652                    computerName: item['Computer Name'] || item['Computer Name '] || '',653                    rawSoftware: rawSoftware,654                    standardTitle: match ? match.standardTitle : rawSoftware,655                    publisher: item['Publisher'] || item['Publisher '] || '',656                    version: item['Version'] || item['Version '] || '',657                    installDate: item['Install Date'] || item['Install Date '] || '',658                    lastInventory: item['Last Inventory Date'] || item['Last Inventory Date '] || '',659                    executablePath: item['Executable Path'] || item['Executable Path '] || '',660                    classification: match ? match.classification : 'Unclassified',661                    licenseType: match ? match.label : 'Unknown',662                    normalized: !!match663                };664            });665        }666 667        function buildRelations() {668            // Link 1: SCCM to Asset via Computer Name669            const assetMap = {};670            dataStore.asset.forEach(a => {671                const compName = a['Computer Name'] || a['Computer Name '] || '';672                if (compName) assetMap[compName.toLowerCase()] = a;673            });674 675            // Link 2: Asset to User (Asset should have User ID)676            const userMap = {};677            dataStore.user.forEach(u => {678                const userId = u['User ID'] || u['User ID '] || '';679                if (userId) userMap[userId.toLowerCase()] = u;680            });681 682            // Build Software per User683            dataStore.softwarePerUser = [];684            dataStore.normalized.forEach(norm => {685                const asset = assetMap[norm.computerName.toLowerCase()];686                if (asset) {687                    const userId = asset['User ID'] || asset['User ID '] || '';688                    const user = userMap[userId.toLowerCase()];689                    if (user) {690                        dataStore.softwarePerUser.push({691                            userId: user['User ID'] || user['User ID '] || '',692                            fullName: user['Full Name'] || user['Full Name '] || '',693                            department: user['Department'] || user['Department '] || '',694                            email: user['Email'] || user['Email '] || '',695                            softwareTitle: norm.rawSoftware,696                            standardTitle: norm.standardTitle,697                            licenseType: norm.licenseType698                        });699                    }700                }701            });702 703            // Build Software per Asset704            dataStore.softwarePerAsset = dataStore.normalized.map(norm => {705                const asset = assetMap[norm.computerName.toLowerCase()] || {};706                return {707                    assetId: asset['Asset ID'] || asset['Asset ID '] || '',708                    serialNumber: asset['Serial Number'] || asset['Serial Number '] || '',709                    computerName: norm.computerName,710                    location: asset['Location'] || asset['Location '] || '',711                    softwareTitle: norm.rawSoftware,712                    standardTitle: norm.standardTitle,713                    version: norm.version,714                    licenseType: norm.licenseType715                };716            }).filter(item => item.assetId);717 718            // Build User-Asset Links719            dataStore.userAssetLink = [];720            dataStore.asset.forEach(asset => {721                const compName = asset['Computer Name'] || asset['Computer Name '] || '';722                const userId = asset['User ID'] || asset['User ID '] || '';723                const user = userMap[userId.toLowerCase()];724                725                if (user) {726                    dataStore.userAssetLink.push({727                        userId: user['User ID'] || user['User ID '] || '',728                        fullName: user['Full Name'] || user['Full Name '] || '',729                        department: user['Department'] || user['Department '] || '',730                        assetId: asset['Asset ID'] || asset['Asset ID '] || '',731                        serialNumber: asset['Serial Number'] || asset['Serial Number '] || '',732                        computerName: compName,733                        location: asset['Location'] || asset['Location '] || '',734                        assetStatus: asset['Asset Status'] || asset['Asset Status '] || ''735                    });736                }737            });738        }739 740        function expandSRData() {741            // SR Expansion: Cartesian Product for each SR742            dataStore.srExpansion = [];743            744            dataStore.sr.forEach(sr => {745                const assetIds = (sr['Requested Asset ID'] || sr['Requested Asset ID '] || '').split(/[,;]/).map(s => s.trim()).filter(s => s);746                const softwareList = (sr['Approved Software'] || sr['Approved Software '] || '').split(/[,;]/).map(s => s.trim()).filter(s => s);747                748                // Cartesian product749                assetIds.forEach(assetId => {750                    softwareList.forEach(software => {751                        dataStore.srExpansion.push({752                            srNumber: sr['SR Number'] || sr['SR Number '] || '',753                            assetId: assetId,754                            approvedSoftware: software,755                            approvalDate: sr['Approval Date'] || sr['Approval Date '] || '',756                            approverEmail: sr['Approver Email'] || sr['Approver Email '] || '',757                            validity: sr['Validity'] || sr['Validity '] || ''758                        });759                    });760                });761            });762        }763 764        // UI Updates765        function updateDashboard() {766            document.getElementById('stat-sccm').textContent = dataStore.sccm.length;767            document.getElementById('stat-assets').textContent = dataStore.asset.length;768            document.getElementById('stat-users').textContent = dataStore.user.length;769            770            const normalized = dataStore.normalized.filter(n => n.normalized).length;771            const total = dataStore.normalized.length;772            const rate = total > 0 ? Math.round((normalized / total) * 100) : 0;773            document.getElementById('stat-norm-rate').textContent = rate + '%';774 775            // Blacklisted776            const blacklisted = dataStore.normalized.filter(n => n.licenseType === 'Blacklisted');777            const blacklistedEl = document.getElementById('blacklisted-list');778            if (blacklisted.length > 0) {779                const unique = [...new Set(blacklisted.map(b => b.standardTitle))];780                blacklistedEl.innerHTML = unique.map(s => 781                    `<div class="flex items-center gap-2 p-2 bg-red-50 rounded-lg">782                        <i data-lucide="ban" class="w-4 h-4 text-red-500"></i>783                        <span class="text-sm text-red-700">${s}</span>784                    </div>`785                ).join('');786            }787 788            // Summary789            const summaryEl = document.getElementById('norm-summary');790            const licenseCounts = {};791            dataStore.normalized.forEach(n => {792                licenseCounts[n.licenseType] = (licenseCounts[n.licenseType] || 0) + 1;793            });794            summaryEl.innerHTML = Object.entries(licenseCounts).map(([type, count]) => 795                `<div class="flex items-center justify-between p-2 bg-slate-50 rounded-lg">796                    <span class="text-sm text-slate-600">${type}</span>797                    <span class="text-sm font-semibold text-slate-800">${count}</span>798                </div>`799            ).join('');800 801            lucide.createIcons();802        }803 804        function renderNormalizedTable() {805            const tbody = document.getElementById('normalized-table-body');806            const data = dataStore.normalized;807            808            if (data.length === 0) {809                tbody.innerHTML = '<tr><td colspan="7" class="text-center py-8 text-slate-400">No data available</td></tr>';810                return;811            }812 813            tbody.innerHTML = data.map(row => `814                <tr class="hover:bg-slate-50 border-b border-slate-100">815                    <td class="font-medium text-slate-800">${row.computerName}</td>816                    <td class="text-slate-600">${row.rawSoftware}</td>817                    <td class="font-medium text-slate-800">${row.standardTitle}</td>818                    <td class="text-slate-600">${row.publisher}</td>819                    <td class="text-slate-600">${row.version}</td>820                    <td>${getLicenseBadge(row.licenseType)}</td>821                    <td>${row.normalized ? '<span class="text-green-600"><i data-lucide="check-circle" class="w-4 h-4"></i></span>' : '<span class="text-amber-600"><i data-lucide="alert-circle" class="w-4 h-4"></i></span>'}</td>822                </tr>823            `).join('');824            825            document.getElementById('normalized-count').textContent = data.length;826            lucide.createIcons();827        }828 829        function getLicenseBadge(type) {830            const classes = {831                'Freeware': 'badge-freeware',832                'License Standard': 'badge-standard',833                'Blacklisted': 'badge-blacklisted',834                'Unknown': 'badge-unknown'835            };836            return `<span class="badge ${classes[type] || 'badge-unknown'}">${type}</span>`;837        }838 839        function renderRelationTables() {840            // Software per User841            const spuBody = document.getElementById('software-per-user-body');842            if (dataStore.softwarePerUser.length > 0) {843                spuBody.innerHTML = dataStore.softwarePerUser.map(row => `844                    <tr class="hover:bg-slate-50 border-b border-slate-100">845                        <td class="font-medium text-slate-800">${row.userId}</td>846                        <td class="text-slate-600">${row.fullName}</td>847                        <td class="text-slate-600">${row.department}</td>848                        <td class="text-slate-600">${row.email}</td>849                        <td class="text-slate-600">${row.softwareTitle}</td>850                        <td class="font-medium text-slate-800">${row.standardTitle}</td>851                        <td>${getLicenseBadge(row.licenseType)}</td>852                    </tr>853                `).join('');854            }855 856            // Software per Asset857            const spaBody = document.getElementById('software-per-asset-body');858            if (dataStore.softwarePerAsset.length > 0) {859                spaBody.innerHTML = dataStore.softwarePerAsset.map(row => `860                    <tr class="hover:bg-slate-50 border-b border-slate-100">861                        <td class="font-medium text-slate-800">${row.assetId}</td>862                        <td class="text-slate-600">${row.serialNumber}</td>863                        <td class="text-slate-600">${row.computerName}</td>864                        <td class="text-slate-600">${row.location}</td>865                        <td class="text-slate-600">${row.softwareTitle}</td>866                        <td class="font-medium text-slate-800">${row.standardTitle}</td>867                        <td class="text-slate-600">${row.version}</td>868                        <td>${getLicenseBadge(row.licenseType)}</td>869                    </tr>870                `).join('');871            }872 873            // User-Asset Link874            const ualBody = document.getElementById('user-asset-link-body');875            if (dataStore.userAssetLink.length > 0) {876                ualBody.innerHTML = dataStore.userAssetLink.map(row => `877                    <tr class="hover:bg-slate-50 border-b border-slate-100">878                        <td class="font-medium text-slate-800">${row.userId}</td>879                        <td class="text-slate-600">${row.fullName}</td>880                        <td class="text-slate-600">${row.department}</td>881                        <td class="font-medium text-slate-800">${row.assetId}</td>882                        <td class="text-slate-600">${row.serialNumber}</td>883                        <td class="text-slate-600">${row.computerName}</td>884                        <td class="text-slate-600">${row.location}</td>885                        <td><span class="badge ${row.assetStatus === 'Active' ? 'badge-freeware' : 'badge-unknown'}">${row.assetStatus}</span></td>886                    </tr>887                `).join('');888            }889        }890 891        function renderSRTable() {892            const tbody = document.getElementById('sr-expansion-body');893            if (dataStore.srExpansion.length > 0) {894                tbody.innerHTML = dataStore.srExpansion.map(row => `895                    <tr class="hover:bg-slate-50 border-b border-slate-100">896                        <td class="font-medium text-slate-800">${row.srNumber}</td>897                        <td class="text-slate-600">${row.assetId}</td>898                        <td class="font-medium text-slate-800">${row.approvedSoftware}</td>899                        <td class="text-slate-600">${row.approvalDate}</td>900                        <td class="text-slate-600">${row.approverEmail}</td>901                        <td>${getLicenseBadge(row.validity === 'Valid' ? 'License Standard' : 'Unknown')}</td>902                    </tr>903                `).join('');904            }905            document.getElementById('sr-expansion-count').textContent = dataStore.srExpansion.length;906        }907 908        // Filtering & Sorting909        function filterNormalizedData() {910            const search = document.getElementById('search-normalized').value.toLowerCase();911            const licenseFilter = document.getElementById('filter-license').value;912            913            let filtered = dataStore.normalized;914            if (search) {915                filtered = filtered.filter(row => 916                    row.computerName.toLowerCase().includes(search) ||917                    row.rawSoftware.toLowerCase().includes(search) ||918                    row.standardTitle.toLowerCase().includes(search) ||919                    row.publisher.toLowerCase().includes(search)920                );921            }922            if (licenseFilter) {923                filtered = filtered.filter(row => row.licenseType === licenseFilter);924            }925 926            const tbody = document.getElementById('normalized-table-body');927            tbody.innerHTML = filtered.map(row => `928                <tr class="hover:bg-slate-50 border-b border-slate-100">929                    <td class="font-medium text-slate-800">${row.computerName}</td>930                    <td class="text-slate-600">${row.rawSoftware}</td>931                    <td class="font-medium text-slate-800">${row.standardTitle}</td>932                    <td class="text-slate-600">${row.publisher}</td>933                    <td class="text-slate-600">${row.version}</td>934                    <td>${getLicenseBadge(row.licenseType)}</td>935                    <td>${row.normalized ? '<span class="text-green-600"><i data-lucide="check-circle" class="w-4 h-4"></i></span>' : '<span class="text-amber-600"><i data-lucide="alert-circle" class="w-4 h-4"></i></span>'}</td>936                </tr>937            `).join('');938            document.getElementById('normalized-count').textContent = filtered.length;939            lucide.createIcons();940        }941 942        function filterRelationData(view) {943            const searchInputs = {944                'software-per-user': 'search-relations-user',945                'software-per-asset': 'search-relations-asset',946                'user-asset-link': 'search-relations-link'947            };948            949            const search = document.getElementById(searchInputs[view]).value.toLowerCase();950            let data, keys;951            952            if (view === 'software-per-user') {953                data = dataStore.softwarePerUser;954                keys = ['userId', 'fullName', 'department', 'email', 'softwareTitle', 'standardTitle'];955            } else if (view === 'software-per-asset') {956                data = dataStore.softwarePerAsset;957                keys = ['assetId', 'serialNumber', 'computerName', 'softwareTitle', 'standardTitle'];958            } else {959                data = dataStore.userAssetLink;960                keys = ['userId', 'fullName', 'department', 'assetId', 'serialNumber', 'computerName'];961            }962            963            const filtered = search ? data.filter(row => 964                keys.some(k => row[k] && row[k].toLowerCase().includes(search))965            ) : data;966            967            renderFilteredRelation(view, filtered);968        }969 970        function renderFilteredRelation(view, data) {971            const tbodyId = {972                'software-per-user': 'software-per-user-body',973                'software-per-asset': 'software-per-asset-body',974                'user-asset-link': 'user-asset-link-body'975            }[view];976            977            const tbody = document.getElementById(tbodyId);978            979            if (view === 'software-per-user') {980                tbody.innerHTML = data.map(row => `981                    <tr class="hover:bg-slate-50 border-b border-slate-100">982                        <td class="font-medium text-slate-800">${row.userId}</td>983                        <td class="text-slate-600">${row.fullName}</td>984                        <td class="text-slate-600">${row.department}</td>985                        <td class="text-slate-600">${row.email}</td>986                        <td class="text-slate-600">${row.softwareTitle}</td>987                        <td class="font-medium text-slate-800">${row.standardTitle}</td>988                        <td>${getLicenseBadge(row.licenseType)}</td>989                    </tr>990                `).join('');991            } else if (view === 'software-per-asset') {992                tbody.innerHTML = data.map(row => `993                    <tr class="hover:bg-slate-50 border-b border-slate-100">994                        <td class="font-medium text-slate-800">${row.assetId}</td>995                        <td class="text-slate-600">${row.serialNumber}</td>996                        <td class="text-slate-600">${row.computerName}</td>997                        <td class="text-slate-600">${row.location}</td>998                        <td class="text-slate-600">${row.softwareTitle}</td>999                        <td class="font-medium text-slate-800">${row.standardTitle}</td>1000                        <td class="text-slate-600">${row.version}</td>1001                        <td>${getLicenseBadge(row.licenseType)}</td>1002                    </tr>1003                `).join('');1004            } else {1005                tbody.innerHTML = data.map(row => `1006                    <tr class="hover:bg-slate-50 border-b border-slate-100">1007                        <td class="font-medium text-slate-800">${row.userId}</td>1008                        <td class="text-slate-600">${row.fullName}</td>1009                        <td class="text-slate-600">${row.department}</td>1010                        <td class="font-medium text-slate-800">${row.assetId}</td>1011                        <td class="text-slate-600">${row.serialNumber}</td>1012                        <td class="text-slate-600">${row.computerName}</td>1013                        <td class="text-slate-600">${row.location}</td>1014                        <td><span class="badge ${row.assetStatus === 'Active' ? 'badge-freeware' : 'badge-unknown'}">${row.assetStatus}</span></td>1015                    </tr>1016                `).join('');1017            }1018        }1019 1020        function filterSRData() {1021            const search = document.getElementById('search-sr').value.toLowerCase();1022            const filtered = search ? dataStore.srExpansion.filter(row => 1023                row.srNumber.toLowerCase().includes(search) ||1024                row.assetId.toLowerCase().includes(search) ||1025                row.approvedSoftware.toLowerCase().includes(search)1026            ) : dataStore.srExpansion;1027            1028            const tbody = document.getElementById('sr-expansion-body');1029            tbody.innerHTML = filtered.map(row => `1030                <tr class="hover:bg-slate-50 border-b border-slate-100">1031                    <td class="font-medium text-slate-800">${row.srNumber}</td>1032                    <td class="text-slate-600">${row.assetId}</td>1033                    <td class="font-medium text-slate-800">${row.approvedSoftware}</td>1034                    <td class="text-slate-600">${row.approvalDate}</td>1035                    <td class="text-slate-600">${row.approverEmail}</td>1036                    <td>${getLicenseBadge(row.validity === 'Valid' ? 'License Standard' : 'Unknown')}</td>1037                </tr>1038            `).join('');1039            document.getElementById('sr-expansion-count').textContent = filtered.length;1040        }1041 1042        function sortTable(tableType, column) {1043            if (sortState.column === column) {1044                sortState.direction = sortState.direction === 'asc' ? 'desc' : 'asc';1045            } else {1046                sortState.column = column;1047                sortState.direction = 'asc';1048            }1049 1050            const data = [...dataStore[tableType === 'normalized' ? 'normalized' : tableType]];1051            data.sort((a, b) => {1052                const valA = a[column] || '';1053                const valB = b[column] || '';1054                if (valA < valB) return sortState.direction === 'asc' ? -1 : 1;1055                if (valA > valB) return sortState.direction === 'asc' ? 1 : -1;1056                return 0;1057            });1058 1059            if (tableType === 'normalized') {1060                dataStore.normalized = data;1061                renderNormalizedTable();1062            }1063        }1064 1065        // Tab Switching1066        function switchTab(tab) {1067            document.querySelectorAll('.view-section').forEach(el => el.classList.add('hidden'));1068            document.getElementById(`view-${tab}`).classList.remove('hidden');1069            1070            document.querySelectorAll('.tab-btn').forEach(el => {1071                el.classList.remove('tab-active');1072                el.classList.add('text-slate-600');1073            });1074            document.getElementById(`tab-${tab}`).classList.add('tab-active');1075            document.getElementById(`tab-${tab}`).classList.remove('text-slate-600');1076        }1077 1078        function switchRelationView(view) {1079            document.querySelectorAll('.relation-view').forEach(el => el.classList.add('hidden'));1080            document.getElementById(`view-${view}`).classList.remove('hidden');1081            1082            document.querySelectorAll('.relation-btn').forEach(el => {1083                el.classList.remove('bg-blue-100', 'text-blue-700', 'font-medium');1084                el.classList.add('bg-slate-100', 'text-slate-600');1085            });1086            document.querySelector(`[data-view="${view}"]`).classList.add('bg-blue-100', 'text-blue-700', 'font-medium');1087            document.querySelector(`[data-view="${view}"]`).classList.remove('bg-slate-100', 'text-slate-600');1088        }1089 1090        // Export1091        function exportData(type) {1092            let data, filename;1093            1094            if (type === 'normalized') {1095                data = dataStore.normalized;1096                filename = 'normalized_data.xlsx';1097            } else if (type === 'software-per-user') {1098                data = dataStore.softwarePerUser;1099                filename = 'software_per_user.xlsx';1100            } else if (type === 'software-per-asset') {1101                data = dataStore.softwarePerAsset;1102                filename = 'software_per_asset.xlsx';1103            } else if (type === 'user-asset-link') {1104                data = dataStore.userAssetLink;1105                filename = 'user_asset_links.xlsx';1106            } else if (type === 'sr-expansion') {1107                data = dataStore.srExpansion;1108                filename = 'sr_expansion.xlsx';1109            }1110 1111            if (data.length === 0) {1112                alert('No data to export');1113                return;1114            }1115 1116            const ws = XLSX.utils.json_to_sheet(data);1117            const wb = XLSX.utils.book_new();1118            XLSX.utils.book_append_sheet(wb, ws, 'Data');1119            XLSX.writeFile(wb, filename);1120        }1121 1122        function exportAllData() {1123            if (dataStore.normalized.length === 0) {1124                alert('No processed data to export. Please upload and process data first.');1125                return;1126            }1127 1128            const wb = XLSX.utils.book_new();1129            1130            if (dataStore.normalized.length > 0) {1131                XLSX.utils.book_append_sheet(wb, XLSX.utils.json_to_sheet(dataStore.normalized), 'Normalized');1132            }1133            if (dataStore.softwarePerUser.length > 0) {1134                XLSX.utils.book_append_sheet(wb, XLSX.utils.json_to_sheet(dataStore.softwarePerUser), 'Software per User');1135            }1136            if (dataStore.softwarePerAsset.length > 0) {1137                XLSX.utils.book_append_sheet(wb, XLSX.utils.json_to_sheet(dataStore.softwarePerAsset), 'Software per Asset');1138            }1139            if (dataStore.userAssetLink.length > 0) {1140                XLSX.utils.book_append_sheet(wb, XLSX.utils.json_to_sheet(dataStore.userAssetLink), 'User Asset Links');1141            }1142            if (dataStore.srExpansion.length > 0) {1143                XLSX.utils.book_append_sheet(wb, XLSX.utils.json_to_sheet(dataStore.srExpansion), 'SR Expansion');1144            }1145 1146            XLSX.writeFile(wb, 'ITSAM_Export_All.xlsx');1147        }1148 1149        function resetAllData() {1150            if (!confirm('Are you sure you want to reset all data?')) return;1151            1152            dataStore = {1153                sccm: [],1154                asset: [],1155                user: [],1156                software: [],1157                sr: [],1158                normalized: [],1159                softwarePerUser: [],1160                softwarePerAsset: [],1161                userAssetLink: [],1162                srExpansion: []1163            };1164 1165            ['sccm', 'asset', 'user', 'software', 'sr'].forEach(type => {1166                document.getElementById(`status-${type}`).classList.add('hidden');1167                document.getElementById(`upload-${type}`).textContent = 'Pending';1168                document.getElementById(`upload-${type}`).className = 'text-sm font-medium text-slate-400';1169            });1170 1171            document.getElementById('stat-sccm').textContent = '0';1172            document.getElementById('stat-assets').textContent = '0';1173            document.getElementById('stat-users').textContent = '0';1174            document.getElementById('stat-norm-rate').textContent = '0%';1175            document.getElementById('blacklisted-list').innerHTML = '<p class="text-sm text-slate-400 italic">No blacklisted software detected</p>';1176            document.getElementById('norm-summary').innerHTML = '<p class="text-sm text-slate-400 italic">Upload data to see summary</p>';1177            1178            document.getElementById('normalized-table-body').innerHTML = '<tr><td colspan="7" class="text-center py-8 text-slate-400">Upload and process data to see normalized results</td></tr>';1179            document.getElementById('software-per-user-body').innerHTML = '<tr><td colspan="7" class="text-center py-8 text-slate-400">Process data to see relations</td></tr>';1180            document.getElementById('software-per-asset-body').innerHTML = '<tr><td colspan="8" class="text-center py-8 text-slate-400">Process data to see relations</td></tr>';1181            document.getElementById('user-asset-link-body').innerHTML = '<tr><td colspan="8" class="text-center py-8 text-slate-400">Process data to see relations</td></tr>';1182            document.getElementById('sr-expansion-body').innerHTML = '<tr><td colspan="6" class="text-center py-8 text-slate-400">Process data to see SR expansions</td></tr>';1183            1184            document.getElementById('normalized-count').textContent = '0';1185            document.getElementById('sr-expansion-count').textContent = '0';1186 1187            switchTab('upload');1188        }1189 1190        function sleep(ms) {1191            return new Promise(resolve => setTimeout(resolve, ms));1192        }1193 1194        // Initialize1195        lucide.createIcons();1196    </script>1197<script src="https://deepsite.hf.co/deepsite-badge.js"></script>1198</body>1199</html>