/* ===== RadiusOntFinder.js (Vue 3 + TT-Core) ===== */ const RadiusOntFinder = { name: 'RadiusOntFinder', template: `
Schritt 1 · Excel (XLSX) Upload

Datei muss die Spalte Serial enthalten. Optional MAC.

{{ uploadError }}
Ergebnisse
{{ processedData.length }} Zeilen verarbeitet
`, data: () => ({ step: 1, parsedData: [], processedData: [], originalHeaders: [], loading: false, progress: 0, currentRow: 0, totalRows: 0, currentSerial: '', uploadError: null, serialColumnName: 'Serial', macColumnName: 'MAC', fetchedKeys: { username: 'fetched_username', customerNumber: 'fetched_customerNumber', customerName: 'fetched_customerName', info: 'fetched_info' }, apiBasePath: window.TT_CONFIG?.BASE_PATH }), methods: { resetComponent() { Object.assign(this.$data, this.$options.data.call(this)); const i = this.$el.querySelector('input[type="file"]'); if (i) i.value = ''; }, async readXlsx(file) { this.uploadError = null; try { await window.TT_CORE.loadScript('https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.17.0/xlsx.full.min.js'); const arr = await new Promise((res, rej) => { const r = new FileReader(); r.onload = e => res(new Uint8Array(e.target.result)); r.onerror = () => rej(new Error('Fehler beim Lesen.')); r.readAsArrayBuffer(file); }); const wb = XLSX.read(arr, {type: 'array'}); const ws = wb.Sheets[wb.SheetNames[0]]; this.parsedData = XLSX.utils.sheet_to_json(ws, {defval: ''}); if (!this.parsedData.length) throw new Error('Die Datei ist leer.'); this.originalHeaders = Object.keys(this.parsedData[0]); if (!this.originalHeaders.includes(this.serialColumnName)) throw new Error(`Erforderliche Spalte '${this.serialColumnName}' nicht gefunden.`); this.startProcessing(); } catch (e) { this.uploadError = e.message; this.step = 1; } }, async startProcessing() { this.step = 2; this.loading = true; this.totalRows = this.parsedData.length; this.processedData = []; const setRow = (row, msg, data = {}) => { const d = { username: `N/A - ${msg}`, customerNumber: 'N/A', customerName: 'N/A', info: 'N/A' }; Object.keys(this.fetchedKeys).forEach(k => row[this.fetchedKeys[k]] = data[k] || d[k]); }; for (const [i, row] of this.parsedData.entries()) { this.currentRow = i; const out = {...row}; const sn = ('' + (row[this.serialColumnName] || '')).trim(); this.currentSerial = `SN: ${sn || '—'}`; let found = false; if (sn) { try { const { data } = await axios.get(`${this.apiBasePath}/Radius/proxyUnsecureHTTPRequestToRadius`, { params: { ont_sn: sn } }); if (Array.isArray(data) && data.length > 0) { setRow(out, '', data[0]); found = true; } } catch { } } if (!found && this.originalHeaders.includes(this.macColumnName)) { const macRaw = ('' + (row[this.macColumnName] || '')).trim(); if (macRaw && macRaw.length === 12) { const mac = macRaw.toUpperCase().match(/.{1,2}/g).join(':'); try { const { data: ses } = await axios.get(`${this.apiBasePath}/Radius/proxyUnsecureHTTPRequestToRadius`, { params: { action2: 'find_by_current_session', mac } }); if (Array.isArray(ses) && ses.length > 0) { const { data: d } = await axios.get(`${this.apiBasePath}/Radius/proxyUnsecureHTTPRequestToRadius`, { params: { username: ses[0], info: '', custnum: '' } }); if (Array.isArray(d) && d.length > 0) { setRow(out, '', d[0]); found = true; } } } catch { } } } if (!found) setRow(out, 'Keinen Benutzer gefunden'); this.processedData.push(out); this.progress = ((i + 1) / this.totalRows) * 100; if ((i + 1) % 20 === 0) await new Promise(r => setTimeout(r, 20)); } this.loading = false; this.currentSerial = ''; }, downloadResults() { if (!this.processedData.length) return; try { const data = this.processedData.map(r => { const o = {}; this.originalHeaders.forEach(h => o[h] = r[h]); Object.keys(this.fetchedKeys).forEach(k => { const K = k.charAt(0).toUpperCase() + k.slice(1).replace('Number', 'nummer').replace('Name', 'name'); o[K] = r[this.fetchedKeys[k]]; }); return o; }); const ws = XLSX.utils.json_to_sheet(data); const wb = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(wb, ws, 'ONT_Finder_Results'); XLSX.writeFile(wb, `ont_finder_results_${new Date().toISOString().replace(/[-:.]/g, '').slice(0, 14)}.xlsx`); } catch { if (window.notify) window.notify('error', 'Fehler beim Erstellen der Excel-Datei.'); } } } }; // Register component with Vue 3 app if (window.VueApp) { window.VueApp.component('radius-ont-finder', RadiusOntFinder); }