Vue.component('tt-chip', { props: { checked: { type: Boolean, default: false } }, template: `
` }); Vue.component('Cpeprovisioning', { template: `
Loading...

Daten werden geladen...

Keine Einträge für die aktuellen Filter gefunden.

{{ item.customer }} #{{ item.owner_customer_number }}
SPIN: {{ item.spin }}
{{ item.network || 'N/A' }}
{{ item.owner_full_address || 'N/A' }}
{{ item.owner_phone }}
{{ item.owner_email }}
Router Konfiguration
Versand & Logistik
Produkt & Services
{{ item.product_name }}
{{ item.product_code }} {{ item.access_type }}
{{ item.access_type_down }} | {{ item.access_type_up }}
Aktionen
Loading...

Extension ID Konfigurieren

Die ID der 'TheTool Helper' Chrome Extension.
`, data() { return { window, loading: true, items: [], filteredItems: [], filters: { network_id: '', routerconfig_finished: '0', hide_delayed_finish: '1', owner: '' }, statusOptions: [ { value: '0', text: 'Offen' }, { value: '1', text: 'Abgeschlossen' } ], delayOptions: [ { value: '1', text: 'Nicht anzeigen' }, { value: '0', text: 'Anzeigen' } ], page: 1, pagination: {}, searchDebounceTimer: null, macInputTimers: {}, extensionId: 'jglijfiddilckddlmbnlojmmlahboffh', showExtensionIdModal: false, processingMacItems: new Set() } }, computed: { networkOptions() { const networks = window.TT_CONFIG.NETWORKS || []; return [{ value: '', text: 'Alle Gebiete' }, ...networks.map(net => ({ value: net.id, text: net.name }))]; }, routerOptions() { return window.TT_CONFIG.ROUTER_OPTIONS || []; } }, created() { // Removed usage of _.debounce // Load Extension ID from local storage const savedExtensionId = localStorage.getItem('radiusExtensionId'); if (savedExtensionId) { this.extensionId = savedExtensionId; } window.addEventListener('keydown', this.handleKeydown); }, beforeDestroy() { window.removeEventListener('keydown', this.handleKeydown); Object.keys(this.macInputTimers).forEach(key => { clearTimeout(this.macInputTimers[key]); }); if (this.searchDebounceTimer) { clearTimeout(this.searchDebounceTimer); } }, methods: { copyToClipboard(text) { if(!text) return; navigator.clipboard.writeText(text) .then(() => window.notify('success', 'Kopiert!')) .catch(() => window.notify('error', 'Fehler beim Kopieren')); }, getRadiusSearchUrl(item) { const custNum = item.owner_customer_number || ''; const basePath = window.TT_CONFIG.BASE_PATH || ''; // If customer number starts with 7000, use ESTMK search mode if (custNum.startsWith('7000')) { return `${basePath}/Radius?estmk_nr=${encodeURIComponent(custNum)}`; } // Otherwise use autocomplete search with custnum return `${basePath}/Radius?custnum=${encodeURIComponent(custNum)}`; }, handleKeydown(e) { // CTRL + ALT + E to open Extension Config if (e.code === 'KeyE' && e.ctrlKey && e.altKey) { e.preventDefault(); this.openExtensionIdModal(); } }, openExtensionIdModal() { this.showExtensionIdModal = true; }, saveExtensionId() { if(this.extensionId) { localStorage.setItem('radiusExtensionId', this.extensionId); this.showExtensionIdModal = false; window.notify('success', 'Extension ID gespeichert.'); } else { window.notify('error', 'Bitte eine ID eingeben.'); } }, isValidMac(mac) { if (!mac) return false; const macRegex = /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/; return macRegex.test(mac); }, handleSearchInput() { if (this.searchDebounceTimer) clearTimeout(this.searchDebounceTimer); this.searchDebounceTimer = setTimeout(() => { this.fetchData(true); }, 400); }, async fetchData(isNewSearch = false) { if (isNewSearch) { this.page = 1; this.items = []; this.filteredItems = []; this.pagination = {}; } if (!isNewSearch && this.pagination.total_pages && this.page > this.pagination.total_pages) { return; } this.loading = true; const payload = { pagination: { page: this.page, per_page: 25 }, filters: { ...this.filters }, order: { key: 'order_id', order: 'desc' } }; try { const { data } = await axios.post(window.TT_CONFIG.CPE_PROV_API_GET_URL, payload); const newItems = (data.rows || []).map(item => ({ ...item, isDirty: false, isSaving: false, pop_name: item.pop_name || 'N/A', owner_address: `${item.owner_street || ''} ${item.owner_housenumber || ''}, ${item.owner_zip || ''} ${item.owner_city || ''}`, owner_phone: item.owner_phone || '', owner_email: item.owner_email || '', })); if (isNewSearch) { this.items = newItems; } else { this.items.push(...newItems); } this.pagination = data.pagination; this.page++; this.filteredItems = this.items; } catch (error) { console.error("Error fetching CPE data:", error); window.notify('error', 'Fehler beim Laden der Daten.'); } finally { this.loading = false; } }, resetFilters() { this.filters = { network_id: '', routerconfig_finished: '0', hide_delayed_finish: '1', owner: '' }; this.fetchData(true); }, markDirty(item) { this.$set(item, 'isDirty', true); }, // --- MAC & QR Logic --- parseMacFromQrCode(qrCode) { if (!qrCode) return null; // Remove whitespace and newlines const cleaned = qrCode.replace(/[\s\n\r]+/g, ''); // Strict Pattern: Must contain a dash separating 6 hex and 12 hex // Example: "CWMP-ID=00040E-802395709D7C" or just "00040E-802395709D7C" const matchDash = cleaned.match(/([0-9A-Fa-f]{6})-([0-9A-Fa-f]{12})/); if (matchDash) { console.log('[MAC Parser] Found Pattern:', matchDash[0]); // RETURN ONLY THE PART AFTER THE DASH (Group 2) return matchDash[2]; } return null; }, calculateMacOffset(macAddress, offset) { // Convert to BigInt to handle 48-bit integer math safely const macDecimal = BigInt('0x' + macAddress); const newMacDecimal = macDecimal + BigInt(offset); let newMacHex = newMacDecimal.toString(16).toUpperCase(); // Ensure 12 chars padding return newMacHex.padStart(12, '0'); }, formatMacAddress(macAddress) { // Strip existing delimiters const cleaned = macAddress.replace(/[:-]/g, ''); // Add colons return cleaned.match(/.{1,2}/g).join(':').toUpperCase(); }, handleMacInput(item, val) { if (val !== undefined) { // Fix encoding issue: replace ß (scharfes s) with - before processing item.cpe_data.mac = val.replace(/ß/g, '-'); } const itemKey = item.orderproduct_id; this.markDirty(item); // Clear existing timer if (this.macInputTimers[itemKey]) { clearTimeout(this.macInputTimers[itemKey]); } // Check if input matches QR code pattern (XXXXXX-XXXXXXXXXXXX) const qrPattern = /[0-9A-Fa-f]{6}-[0-9A-Fa-f]{12}/; const hasCompleteQr = qrPattern.test(item.cpe_data.mac || ''); if (hasCompleteQr) { // Complete QR code detected - short delay to ensure full scan is received this.macInputTimers[itemKey] = setTimeout(() => { this.processMacAddress(item); }, 150); } else { // Manual entry or incomplete scan - longer debounce this.macInputTimers[itemKey] = setTimeout(() => { this.processMacAddress(item); }, 600); } }, handleRouterTypeChange(item) { // Re-process MAC if router type changes (offset might change) if (item.cpe_data.mac) { this.processMacAddress(item); } }, processMacAddress(item) { let inputValue = item.cpe_data.mac; const routerType = item.cpe_data.routertype; if (!inputValue) return; // Fix encoding issue: replace ß (scharfes s) with - before processing if (inputValue.includes('ß')) { inputValue = inputValue.replace(/ß/g, '-'); this.$set(item.cpe_data, 'mac', inputValue); // Update field with corrected value } // Only process QR codes for FritzBox 4050 and 7690 if (routerType === 'FritzBox 4050' || routerType === 'FritzBox 7690') { const parsedMac = this.parseMacFromQrCode(inputValue); if (parsedMac) { // QR code pattern found, calculate offset and format let offset = 0; if (routerType === 'FritzBox 4050') offset = -3; else if (routerType === 'FritzBox 7690') offset = 2; try { const newMac = (offset !== 0) ? this.calculateMacOffset(parsedMac, offset) : parsedMac; const formatted = this.formatMacAddress(newMac); if (item.cpe_data.mac !== formatted) { this.$set(item.cpe_data, 'mac', formatted); const offsetStr = offset > 0 ? `+${offset}` : `${offset}`; window.notify('success', `MAC berechnet (${offsetStr}): ${formatted}`); } } catch (e) { console.error('MAC Calculation error', e); window.notify('error', 'Fehler bei MAC Berechnung'); } return; // Exit after QR processing } } // For all router types (including 4050/7690 without QR): format manual entry const cleanInput = inputValue.replace(/[:-]/g, '').replace(/\s/g, ''); if (cleanInput.length === 12 && /^[0-9A-Fa-f]{12}$/.test(cleanInput)) { const formatted = this.formatMacAddress(cleanInput); if (item.cpe_data.mac !== formatted) { this.$set(item.cpe_data, 'mac', formatted); } } }, checkShipping(item) { if (item.cpe_data.shipping && item.cpe_data.routertype) { const shippingData = this.window.TT_CONFIG.ROUTER_SHIPPING_DATA ? this.window.TT_CONFIG.ROUTER_SHIPPING_DATA[item.cpe_data.routertype] : null; if (shippingData) { item.cpe_data.ship_weight = shippingData.weight; item.cpe_data.ship_length = shippingData.length; item.cpe_data.ship_width = shippingData.width; item.cpe_data.ship_height = shippingData.height; item.cpe_data = { ...item.cpe_data }; // Trigger reactivity this.window.notify('success', 'Versanddaten übernommen.'); } } }, createRadiusUser(item) { window.notify('info', 'Sende Daten an Chrome Extension...'); const message = { type: "CREATE_RADIUS_USER", payload: { customerNumber: item.owner_customer_number || 'N/A', macAddress: item.cpe_data.mac, address: item.owner_full_address || 'N/A', customerName: item.customer || 'N/A', productName: item.product_name || 'N/A' } }; if (window.chrome && chrome.runtime && chrome.runtime.sendMessage) { try { chrome.runtime.sendMessage(this.extensionId, message, (response) => { if (chrome.runtime.lastError) { console.warn(chrome.runtime.lastError.message); window.notify('warning', 'Kommunikation fehlgeschlagen. Extension installiert?'); } else { window.notify('success', 'Daten gesendet!'); } }); } catch (e) { window.notify('error', 'Fehler: ' + e.message); } } else { window.notify('warning', 'Chrome Messaging API nicht verfügbar.'); } }, _buildSavePayload(item) { return { id: item.cpe_id, order_id: item.order_id, orderproduct_id: item.orderproduct_id, termination_id: item.termination_id, ont_sn: item.ont_sn, vlans: item.vlans, ...item.cpe_data, routertype: item.cpe_data.routertype || '', // Ensure empty string instead of null shipping: item.cpe_data.shipping ? 1 : 0, routerconfig_finished: item.cpe_data.routerconfig_finished ? 1 : 0, }; }, async saveCpe(item) { this.$set(item, 'isSaving', true); const payload = this._buildSavePayload(item); try { const { data } = await axios.post(this.window.TT_CONFIG.CPE_PROV_API_SAVE_URL, payload); if (data.success) { this.window.notify('success', data.message); if (this.filters.routerconfig_finished === '0' && payload.routerconfig_finished) { this.items = this.items.filter(i => i.orderproduct_id !== item.orderproduct_id); this.filteredItems = this.items; } else { const index = this.items.findIndex(i => i.orderproduct_id === item.orderproduct_id); if (index !== -1) { this.$set(this.items[index], 'isDirty', false); } } } else { this.window.notify('error', data.message || 'Fehler beim Speichern.'); } } catch (error) { this.window.notify('error', 'Ein unerwarteter Fehler ist aufgetreten.'); } finally { this.$set(item, 'isSaving', false); } } }, mounted() { this.fetchData(true); } });