diff --git a/public/js/pages/Cpeprovisioning/Cpeprovisioning.css b/public/js/pages/Cpeprovisioning/Cpeprovisioning.css index 0456872a3..681de24eb 100644 --- a/public/js/pages/Cpeprovisioning/Cpeprovisioning.css +++ b/public/js/pages/Cpeprovisioning/Cpeprovisioning.css @@ -1,20 +1,33 @@ /* Cpeprovisioning.css */ body { - overflow: hidden; + overflow-y: auto; } /* --- Page & Filter Layout --- */ +.cpe-provisioning-page { + padding-bottom: 2rem; +} + +.cpe-provisioning-page .filter-wrapper { + background: #fff; + padding: 1rem; + border-radius: 4px; + box-shadow: 0 1px 3px rgba(0,0,0,0.1); + margin-bottom: 1.5rem; +} + .cpe-provisioning-page .filter-grid { display: grid; - grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem; align-items: end; } + .cpe-provisioning-page .filter-actions { display: flex; gap: 0.5rem; - padding-bottom: 1rem; } + .loading-indicator, .no-results-indicator { text-align: center; padding: 4rem 2rem; @@ -23,138 +36,264 @@ body { /* --- Cards Container --- */ .cpe-cards-container { - display: grid; - grid-template-columns: 1fr; + display: flex; + flex-direction: column; gap: 1rem; - margin-top: 1.5rem; } /* --- Single Card Styling --- */ .cpe-card { background-color: #fff; border: 1px solid #dee2e6; - border-left: 5px solid transparent; - border-radius: 0.5rem; - transition: box-shadow 0.2s ease-in-out, border-color 0.2s ease-in-out; + border-left: 4px solid #adb5bd; + border-radius: 4px; + box-shadow: 0 1px 2px rgba(0,0,0,0.05); + transition: all 0.2s ease; } .cpe-card.is-dirty { - border-left-color: #f7c423; /* Yellow accent for dirty */ + border-left-color: #fcc419; /* Yellow accent for dirty */ } .cpe-card:hover { - box-shadow: 0 4px 12px rgba(0,0,0,0.08); + box-shadow: 0 4px 12px rgba(0,0,0,0.1); + transform: translateY(-1px); } /* --- Card Header --- */ .cpe-card-header { - display: grid; - grid-template-columns: minmax(200px, 1.5fr) 2fr auto; + display: flex; + justify-content: space-between; align-items: center; - gap: 1.5rem; - padding: 0.75rem 1rem; + padding: 0.5rem 1rem; background-color: #f8f9fa; border-bottom: 1px solid #e9ecef; + gap: 1rem; + flex-wrap: wrap; } -.cpe-card-header .customer-info strong { - color: #005384; - font-size: 1.1rem; + +.customer-info { + flex: 1; + min-width: 250px; } -.cpe-card-header .customer-info small { - display: block; - font-size: 0.8rem; +.customer-info .name { + color: #1864ab; + font-weight: 600; + font-size: 1.05rem; + margin-right: 0.5rem; } -.location-contact-header { +.customer-info .meta { font-size: 0.85rem; color: #495057; - display: grid; - grid-template-columns: repeat(2, 1fr); - gap: 0.1rem 1rem; } + +.location-info { + flex: 2; + display: flex; + flex-wrap: wrap; + gap: 0.5rem 1.5rem; + font-size: 0.85rem; + color: #495057; +} +.location-info div { + display: flex; + align-items: center; +} +.location-info i { + margin-right: 0.4rem; + color: #adb5bd; +} + .header-actions { display: flex; align-items: center; - gap: 1rem; - font-size: 1.1rem; - color: #005384; + gap: 0.75rem; + font-size: 1rem; } -.header-actions a { color: inherit; transition: color 0.2s; } -.header-actions a:hover { color: #f7c423; } +.header-actions a, .header-actions span { + color: #868e96; + transition: color 0.2s; + cursor: pointer; +} +.header-actions a:hover, .header-actions span:hover { color: #228be6; } +.header-actions .text-purple { color: #be4bdb; } /* --- Card Content Grid --- */ .cpe-card-content { - padding: 0.75rem 1rem; + padding: 1rem; display: grid; - grid-template-columns: repeat(4, minmax(280px, 1fr)); /* Changed to 4 columns */ - gap: 1rem 1.5rem; + grid-template-columns: repeat(4, 1fr); + gap: 1.5rem; } .content-column { - padding-top: 0.5rem; display: flex; flex-direction: column; - gap: 0.75rem; -} -.content-column.action-column { - justify-content: space-between; + gap: 0.5rem; + min-width: 0; /* Prevent overflow */ } + .content-column h5 { - font-size: 0.8rem; - font-weight: 600; - color: #005384; + font-size: 0.75rem; + font-weight: 700; + color: #868e96; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 0.25rem; + border-bottom: 1px solid #f1f3f5; padding-bottom: 0.25rem; - border-bottom: 1px solid #e9ecef; } -.content-column p { - font-size: 0.9rem; - margin-bottom: 0.25rem; - line-height: 1.4; + +.content-column .form-group { + margin-bottom: 0.5rem; } -.content-column .form-group, .content-column .tt-select-modern { - margin-bottom: 0; +.content-column label { + font-size: 0.75rem; + color: #495057; + margin-bottom: 0.1rem; + font-weight: 500; } -.action-column .btn { - width: 100%; - margin-top: 0.5rem; +.content-column .form-control, .content-column .custom-select { + font-size: 0.85rem; + padding: 0.25rem 0.5rem; + height: auto; } + +/* Specific Column Tweaks */ .shipping-dims { display: grid; grid-template-columns: repeat(2, 1fr); - gap: 0.5rem 0.75rem; + gap: 0.5rem; } -.finish-wrapper { + +.product-info { + background: #f8f9fa; + padding: 0.5rem; + border-radius: 4px; + font-size: 0.85rem; +} +.product-name { + font-weight: 600; + color: #343a40; + display: block; + margin-bottom: 0.25rem; +} +.product-badges { + display: flex; + gap: 0.5rem; + font-size: 0.75rem; +} + +.vlans-container { + display: flex; + flex-wrap: wrap; + gap: 0.4rem; + margin-top: 0.5rem; +} +.vlan-chip { + font-size: 0.75rem; + padding: 2px 8px; + background: #e7f5ff; + color: #1971c2; + border: 1px solid #d0ebff; + border-radius: 12px; + cursor: pointer; + display: flex; + align-items: center; + gap: 4px; + transition: all 0.1s; +} +.vlan-chip:hover { + background: #d0ebff; +} +.vlan-chip input { + margin: 0; +} + +/* Action Column */ +.action-column { + justify-content: space-between; +} +.finish-toggle { display: flex; align-items: center; justify-content: space-between; - padding: 0.25rem; - background-color: #f1f3f5; - border-radius: 0.25rem; + background: #f8f9fa; + padding: 0.5rem; + border-radius: 4px; + margin-top: auto; + margin-bottom: 0.5rem; } -.mt-auto { - margin-top: auto !important; +.finish-toggle label { margin: 0; } + +/* --- Custom Modal --- */ +.custom-modal-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0,0,0,0.5); + z-index: 1050; + display: flex; + align-items: center; + justify-content: center; +} +.custom-modal-container { + background: #fff; + border-radius: 6px; + box-shadow: 0 10px 25px rgba(0,0,0,0.2); + width: 90%; + max-width: 500px; + overflow: hidden; + animation: modalSlideIn 0.2s ease-out; +} +@keyframes modalSlideIn { + from { opacity: 0; transform: translateY(-20px); } + to { opacity: 1; transform: translateY(0); } +} +.custom-modal-header { + padding: 1rem; + background: #f8f9fa; + border-bottom: 1px solid #e9ecef; + display: flex; + justify-content: space-between; + align-items: center; +} +.custom-modal-header h3 { + margin: 0; + font-size: 1.1rem; + color: #343a40; +} +.custom-modal-close { + background: none; + border: none; + font-size: 1.5rem; + line-height: 1; + color: #adb5bd; + cursor: pointer; +} +.custom-modal-close:hover { color: #495057; } +.custom-modal-body { + padding: 1.5rem; +} +.custom-modal-footer { + padding: 1rem; + border-top: 1px solid #e9ecef; + text-align: right; + background: #f8f9fa; } -/* --- VLAN Chip Styling --- */ -.vlans-container { display: flex; flex-wrap: wrap; gap: 0.5rem; } -.tt-chip { display: inline-flex; align-items: center; padding: 4px 10px; border-radius: 16px; background-color: #f1f3f5; border: 1px solid #dee2e6; font-size: 0.8em; white-space: nowrap; } -.tt-chip.is-checked { background-color: #e7f5ff; border-color: #a5d8ff; color: #1c7ed6; font-weight: 500; } -.tt-chip > * + * { margin-left: 6px; } -.tt-chip input[type="checkbox"] { margin: 0; cursor: pointer; } - -/* Responsive adjustments */ +/* Responsive */ @media (max-width: 1400px) { .cpe-card-content { - grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); + grid-template-columns: 1fr 1fr; } } -@media (max-width: 992px) { - .cpe-card-header { +@media (max-width: 768px) { + .cpe-card-content { grid-template-columns: 1fr; - gap: 0.75rem; } -} - -.tt-switch { - margin-bottom: 0 !important; + .cpe-card-header { + flex-direction: column; + align-items: flex-start; + } } \ No newline at end of file diff --git a/public/js/pages/Cpeprovisioning/Cpeprovisioning.js b/public/js/pages/Cpeprovisioning/Cpeprovisioning.js index 9d01244d0..63e1ad23e 100644 --- a/public/js/pages/Cpeprovisioning/Cpeprovisioning.js +++ b/public/js/pages/Cpeprovisioning/Cpeprovisioning.js @@ -8,18 +8,18 @@ Vue.component('tt-chip', { Vue.component('Cpeprovisioning', { template: `
- -
- - - - -
- - +
+
+ + + + +
+ + +
-
- +
Loading...
@@ -35,17 +35,20 @@ Vue.component('Cpeprovisioning', {
- - {{ item.customer }}#{{ item.owner_customer_number }} - - SPIN: {{ item.spin }} +
+ {{ item.customer }} + #{{ item.owner_customer_number }} +
+ SPIN: {{ item.spin }}
-
-
Netzgebiet: {{ item.network || 'N/A' }}
-
{{ item.owner_phone }}
-
Adresse: {{ item.owner_full_address || 'N/A' }}
-
{{ item.owner_email }}
+ +
+
{{ item.network || 'N/A' }}
+
{{ item.owner_full_address || 'N/A' }}
+
{{ item.owner_phone }}
+
{{ item.owner_email }}
+
+
Router Konfiguration
- - - -
- -
- + + + + +
+
-
Versand & Abschluss
- -
- - - - +
Versand & Logistik
+
+ +
- +
+ + + + +
+
+
-
Produkt & VLANs
-

{{ item.product_name }} {{ item.product_code }}

-

- {{ item.access_type }} - {{ item.access_type_down }} - {{ item.access_type_up }} -

-
+
Produkt & Services
+
+ {{ item.product_name }} +
+ {{ item.product_code }} + {{ item.access_type }} +
+
+ {{ item.access_type_down }} | {{ item.access_type_up }} +
+
+ +
+
Aktionen
-
- + + sm block + additional-class="btn-outline-primary mb-2" + icon="fas fa-user-plus" + title="An Chrome Extension senden" />
-
- - + +
+
+ + +
+
-
-
-
- Loading... -
- Lade weitere Einträge... -
+
Loading...
- -
-
-
-
+ +
+
+
+

Extension ID Konfigurieren

+ +
+
+
+ +
+
+ +
+ +
+ Die ID der 'TheTool Helper' Chrome Extension. +
+
+
-
- +
+
`, data() { @@ -171,11 +212,11 @@ Vue.component('Cpeprovisioning', { delayOptions: [ { value: '1', text: 'Nicht anzeigen' }, { value: '0', text: 'Anzeigen' } ], page: 1, pagination: {}, - debouncedFetchData: null, - macInputTimers: {}, // Store timers for debouncing MAC input + searchDebounceTimer: null, + macInputTimers: {}, extensionId: 'jglijfiddilckddlmbnlojmmlahboffh', showExtensionIdModal: false, - processingMacItems: new Set() // Track items currently being processed + processingMacItems: new Set() } }, computed: { @@ -188,7 +229,8 @@ Vue.component('Cpeprovisioning', { } }, created() { - this.debouncedFetchData = _.debounce(this.fetchData.bind(this, true), 400); + // Removed usage of _.debounce + // Load Extension ID from local storage const savedExtensionId = localStorage.getItem('radiusExtensionId'); if (savedExtensionId) { this.extensionId = savedExtensionId; @@ -197,23 +239,22 @@ Vue.component('Cpeprovisioning', { }, beforeDestroy() { window.removeEventListener('keydown', this.handleKeydown); - // Clear all pending MAC input timers 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', 'Text erfolgreich in die Zwischenablage kopiert!'); - }) - .catch(err => { - console.error('Fehler beim Kopieren des Textes: ', err); - window.notify('error', 'Fehler beim Kopieren des Textes.'); - }); + .then(() => window.notify('success', 'Kopiert!')) + .catch(() => window.notify('error', 'Fehler beim Kopieren')); }, handleKeydown(e) { + // CTRL + ALT + E to open Extension Config if (e.code === 'KeyE' && e.ctrlKey && e.altKey) { e.preventDefault(); this.openExtensionIdModal(); @@ -223,15 +264,25 @@ Vue.component('Cpeprovisioning', { this.showExtensionIdModal = true; }, saveExtensionId() { - localStorage.setItem('radiusExtensionId', this.extensionId); - this.showExtensionIdModal = false; - window.notify('success', 'Extension ID gespeichert.'); + 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; @@ -288,278 +339,175 @@ Vue.component('Cpeprovisioning', { markDirty(item) { this.$set(item, 'isDirty', true); }, + + // --- MAC & QR Logic --- + parseMacFromQrCode(qrCode) { - console.log('[MAC Parser] Raw QR Code input:', qrCode); - - // Remove any whitespace/newlines (barcode scanner input doesn't support newlines, but just in case) - const cleaned = qrCode.replace(/\s+/g, ''); - console.log('[MAC Parser] Cleaned QR Code:', cleaned); - - // Extract MAC address from QR format: 00040E-802395709D7C - // The MAC address is the 12 hex characters AFTER the dash - const match = cleaned.match(/-([0-9A-Fa-f]{12})/); - console.log('[MAC Parser] Regex match result:', match); - - if (match) { - const macAddress = match[1]; // Get only the part after the dash: 802395709D7C - console.log('[MAC Parser] Extracted MAC:', macAddress); - return macAddress; + 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]; } - - console.log('[MAC Parser] No match found, returning null'); + return null; }, + calculateMacOffset(macAddress, offset) { - console.log('[MAC Offset] Input MAC:', macAddress, 'Offset:', offset); - - // Convert MAC to decimal using BigInt for large numbers + // Convert to BigInt to handle 48-bit integer math safely const macDecimal = BigInt('0x' + macAddress); - console.log('[MAC Offset] MAC as decimal:', macDecimal.toString()); - - // Apply offset (add or subtract) const newMacDecimal = macDecimal + BigInt(offset); - console.log('[MAC Offset] New MAC as decimal:', newMacDecimal.toString()); - - // Convert back to hex (12 chars, padded with zeros) let newMacHex = newMacDecimal.toString(16).toUpperCase(); - newMacHex = newMacHex.padStart(12, '0'); - console.log('[MAC Offset] Result MAC:', newMacHex); - - return newMacHex; + // Ensure 12 chars padding + return newMacHex.padStart(12, '0'); }, + formatMacAddress(macAddress) { - console.log('[MAC Format] Input MAC:', macAddress); - - // Remove any existing formatting + // Strip existing delimiters const cleaned = macAddress.replace(/[:-]/g, ''); - console.log('[MAC Format] Cleaned MAC:', cleaned); - - // Format as AA:BB:CC:DD:EE:FF (uppercase) - const formatted = cleaned.match(/.{1,2}/g).join(':').toUpperCase(); - console.log('[MAC Format] Formatted MAC:', formatted); - - return formatted; + // Add colons + return cleaned.match(/.{1,2}/g).join(':').toUpperCase(); }, + handleMacInput(item, val) { if (val !== undefined) { item.cpe_data.mac = val; } - const currentValue = item.cpe_data.mac; - - console.log('[MAC Input] === START handleMacInput ==='); - console.log('[MAC Input] Item:', item); - console.log('[MAC Input] Current MAC value:', currentValue); - console.log('[MAC Input] Router type:', item.cpe_data.routertype); - + const itemKey = item.orderproduct_id; - - // Check if we're already processing this item to prevent recursion - if (this.processingMacItems.has(itemKey)) { - console.log('[MAC Input] Already processing this item, returning to prevent recursion'); - return; - } - + this.markDirty(item); - - // Clear existing timer for this item + if (this.macInputTimers[itemKey]) { - console.log('[MAC Input] Clearing existing timer for item:', itemKey); clearTimeout(this.macInputTimers[itemKey]); } - - // Check for immediate QR code match (CWMP ID format) - // Format: 00040E-802395709D7C (approx 19 chars) - if (currentValue && currentValue.includes('-') && currentValue.length >= 18) { - const quickMatch = /-([0-9A-Fa-f]{12})/.test(currentValue); - if (quickMatch) { - console.log('[MAC Input] Quick match found, processing immediately'); - this.processMacAddress(item); - return; - } + + // Immediate check if it LOOKS like it contains our pattern + const isPotentialScan = item.cpe_data.mac && item.cpe_data.mac.includes('-'); + + if (isPotentialScan) { + // If it has a dash, we process immediately to see if it matches our strict pattern + this.processMacAddress(item); + } else { + // Otherwise debounce (typing manual address) + this.macInputTimers[itemKey] = setTimeout(() => { + this.processMacAddress(item); + }, 300); } - - // Create new timer (debounce with 300ms delay) - console.log('[MAC Input] Creating new debounce timer for item:', itemKey); - this.macInputTimers[itemKey] = setTimeout(() => { - console.log('[MAC Input] Debounce timer fired for item:', itemKey); - this.processMacAddress(item); - }, 300); // 300ms delay to wait for barcode scanner to finish }, + handleRouterTypeChange(item) { - console.log('[Router Type Change] Router type changed, checking if MAC needs processing'); - this.processMacAddress(item); + // Re-process MAC if router type changes (offset might change) + if (item.cpe_data.mac) { + this.processMacAddress(item); + } }, + processMacAddress(item) { const inputValue = item.cpe_data.mac; const routerType = item.cpe_data.routertype; - const itemKey = item.orderproduct_id; - console.log('[MAC Process] === START processMacAddress ==='); - console.log('[MAC Process] Input value:', inputValue); - console.log('[MAC Process] Router type:', routerType); + if (!inputValue) return; - // Only process if it's a QR code format (contains dash, no colons) - // This prevents reprocessing already formatted MAC addresses - if (!inputValue) { - console.log('[MAC Process] No input value, returning'); - return; - } + // 1. Try to detect and parse QR code format + // This will ONLY return a value if the XXXXXX-XXXXXXXXXXXX pattern exists + const parsedMac = this.parseMacFromQrCode(inputValue); + + if (parsedMac) { + // A QR-like pattern (XXXXXX-XXXXXXXXXXXX) was found + if (routerType === 'FritzBox 4050' || routerType === 'FritzBox 7690') { + // Perform calculation and full formatting for specific routers + let offset = 0; + if (routerType === 'FritzBox 4050') offset = -3; + else if (routerType === 'FritzBox 7690') offset = 2; - if (!inputValue.includes('-')) { - console.log('[MAC Process] No dash found in input, returning'); - return; - } + try { + const newMac = (offset !== 0) ? this.calculateMacOffset(parsedMac, offset) : parsedMac; + const formatted = this.formatMacAddress(newMac); - if (inputValue.includes(':')) { - console.log('[MAC Process] Already formatted (contains colons), returning'); - return; - } - - console.log('[MAC Process] Input contains dash and no colons, proceeding...'); - - // Check if this is a 4050 or 7690 router - if (routerType !== 'FritzBox 4050' && routerType !== 'FritzBox 7690') { - console.log('[MAC Process] Router type is not FritzBox 4050 or FritzBox 7690, returning. Current type:', routerType); - return; - } - - console.log('[MAC Process] Router type is valid:', routerType); - - // Mark this item as being processed - this.processingMacItems.add(itemKey); - console.log('[MAC Process] Added item to processing set:', itemKey); - - // Safety timeout to ensure we don't get stuck in processing state - const safetyTimeout = setTimeout(() => { - console.log('[MAC Process] Safety timeout - removing item from processing set:', itemKey); - this.processingMacItems.delete(itemKey); - }, 2000); - - try { - // Parse MAC from QR code - const parsedMac = this.parseMacFromQrCode(inputValue); - - if (!parsedMac) { - console.log('[MAC Process] Failed to parse MAC from QR code'); - window.notify('error', 'Konnte MAC-Adresse nicht aus QR-Code parsen'); - clearTimeout(safetyTimeout); - this.processingMacItems.delete(itemKey); - return; + 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); + } + } else { + // For other routers, just extract the 12-char MAC from QR, but don't apply offset. + // The 'formatting' part (AA:BB:CC...) should only happen for 4050/7690 + // So for other types, we'll just put the raw 12-char MAC from the QR. + if (item.cpe_data.mac !== parsedMac) { + this.$set(item.cpe_data, 'mac', parsedMac); // Store raw 12-char MAC + window.notify('info', 'MAC aus QR extrahiert (keine Berechnung für diesen Routertyp).'); + } + } + } else { + // No QR pattern found, apply general manual entry formatting if it's a valid 12-char hex string + 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); + window.notify('info', 'MAC formatiert.'); + } } - - console.log('[MAC Process] Successfully parsed MAC:', parsedMac); - - // Calculate offset based on router type - const offset = routerType === 'FritzBox 4050' ? -3 : 2; - console.log('[MAC Process] Applying offset:', offset); - - const newMac = this.calculateMacOffset(parsedMac, offset); - console.log('[MAC Process] Calculated new MAC:', newMac); - - // Format MAC address - const formattedMac = this.formatMacAddress(newMac); - console.log('[MAC Process] Formatted MAC:', formattedMac); - - // Update the field using Vue.set for proper reactivity - console.log('[MAC Process] Updating item.cpe_data.mac to:', formattedMac); - this.$set(item.cpe_data, 'mac', formattedMac); - - // Force Vue to update the DOM - this.$nextTick(() => { - console.log('[MAC Process] After nextTick, MAC value is:', item.cpe_data.mac); - // Clear safety timeout and remove from processing set - clearTimeout(safetyTimeout); - this.processingMacItems.delete(itemKey); - console.log('[MAC Process] Removed item from processing set after nextTick:', itemKey); - }); - - // Show notification - const offsetStr = offset > 0 ? `+${offset}` : `${offset}`; - window.notify('success', `MAC-Adresse aus QR-Code geparst (${offsetStr}): ${formattedMac}`); - - } catch (error) { - console.error('[MAC Process] Error processing MAC:', error); - clearTimeout(safetyTimeout); - this.processingMacItems.delete(itemKey); } - - console.log('[MAC Process] === END processMacAddress ==='); }, + checkShipping(item) { if (item.cpe_data.shipping && item.cpe_data.routertype) { - const shippingData = this.window.TT_CONFIG.ROUTER_SHIPPING_DATA[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 wurden automatisch ausgefüllt.'); + this.window.notify('success', 'Versanddaten übernommen.'); } - } else if (!item.cpe_data.shipping) { - item.cpe_data.ship_weight = ''; - item.cpe_data.ship_length = ''; - item.cpe_data.ship_width = ''; - item.cpe_data.ship_height = ''; - item.cpe_data = { ...item.cpe_data }; // Trigger reactivity } }, + createRadiusUser(item) { - console.log('[Create Radius User] === START ==='); - console.log('[Create Radius User] Item:', item); - - // Prepare the data to send to the Chrome extension - const customerNumber = item.owner_customer_number || 'N/A'; - const macAddress = item.cpe_data.mac; - const address = item.owner_full_address || 'N/A'; - const customerName = item.customer || 'N/A'; - const productName = item.product_name || 'N/A'; - - console.log('[Create Radius User] Customer Number:', customerNumber); - console.log('[Create Radius User] MAC Address:', macAddress); - console.log('[Create Radius User] Address:', address); - console.log('[Create Radius User] Customer Name:', customerName); - console.log('[Create Radius User] Product Name:', productName); - window.notify('info', 'Sende Daten an Chrome Extension...'); - - const extensionId = this.extensionId; + const message = { type: "CREATE_RADIUS_USER", payload: { - customerNumber: customerNumber, - macAddress: macAddress, - address: address, - customerName: customerName, - productName: productName + 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' } }; - console.log('[Create Radius User] Extension ID:', extensionId); - console.log('[Create Radius User] Message:', message); - if (window.chrome && chrome.runtime && chrome.runtime.sendMessage) { try { - chrome.runtime.sendMessage(extensionId, message, (response) => { + chrome.runtime.sendMessage(this.extensionId, message, (response) => { if (chrome.runtime.lastError) { - console.warn('[Create Radius User] Senden an Erweiterung fehlgeschlagen:', chrome.runtime.lastError.message); - window.notify('warning', 'Daten konnten nicht an die Erweiterung gesendet werden. (Drücke STRG + ALT + E zum Konfigurieren)'); + console.warn(chrome.runtime.lastError.message); + window.notify('warning', 'Kommunikation fehlgeschlagen. Extension installiert?'); } else { - console.log('[Create Radius User] Erweiterung hat geantwortet:', response); - window.notify('success', 'Daten erfolgreich an Chrome Extension gesendet!'); + window.notify('success', 'Daten gesendet!'); } }); } catch (e) { - console.error('[Create Radius User] Fehler beim Senden an die Erweiterung:', e); - window.notify('error', 'Fehler beim Senden an die Erweiterung.'); + window.notify('error', 'Fehler: ' + e.message); } } else { - console.warn('[Create Radius User] Chrome Extension Messaging API nicht verfügbar.'); - window.notify('warning', 'Chrome Messaging API nicht gefunden.'); + window.notify('warning', 'Chrome Messaging API nicht verfügbar.'); } - - console.log('[Create Radius User] === END ==='); }, + _buildSavePayload(item) { return { id: item.cpe_id, @@ -573,6 +521,7 @@ Vue.component('Cpeprovisioning', { routerconfig_finished: item.cpe_data.routerconfig_finished ? 1 : 0, }; }, + async saveCpe(item) { this.$set(item, 'isSaving', true); const payload = this._buildSavePayload(item); @@ -602,6 +551,5 @@ Vue.component('Cpeprovisioning', { }, mounted() { this.fetchData(true); - window.addEventListener('keydown', this.handleKeydown); } });