/** @odoo-module **/ import { Component, useState, onWillStart, onWillUnmount } from "@odoo/owl"; import { useService } from "@web/core/utils/hooks"; import { BluetoothPrinterConfig } from "./bluetooth_printer_config"; import { ConfirmationDialog } from "@web/core/confirmation_dialog/confirmation_dialog"; import { BluetoothPrinterStorage } from "./storage_manager"; /** * Bluetooth Connection Status Widget * * Displays the current connection status of the bluetooth thermal printer * with visual indicators and detailed tooltip information. * * Status states: * - connected: Green indicator, printer is connected and ready * - disconnected: Red indicator, printer is not connected * - connecting: Yellow indicator with animation, attempting to connect * - error: Red indicator with error icon, connection error occurred */ export class BluetoothConnectionStatus extends Component { static template = "pos_bluetooth_thermal_printer.BluetoothConnectionStatus"; setup() { this.dialog = useService("dialog"); this.notification = useService("notification"); this.storageManager = new BluetoothPrinterStorage(); this.state = useState({ status: 'disconnected', deviceName: null, lastError: null, reconnectAttempts: 0, isReconnecting: false, timestamp: null, showTooltip: false }); // Get the bluetooth printer manager service // This will be injected when the component is used in the POS this.bluetoothManager = this.props.bluetoothManager; onWillStart(() => { this._initializeStatus(); this._subscribeToEvents(); }); onWillUnmount(() => { this._unsubscribeFromEvents(); }); } /** * Initialize status from bluetooth manager * @private */ _initializeStatus() { if (this.bluetoothManager) { const info = this.bluetoothManager.getConnectionInfo(); this._updateStatus(info); } } /** * Subscribe to bluetooth manager events * @private */ _subscribeToEvents() { if (this.bluetoothManager) { this._statusChangeHandler = (data) => this._onStatusChanged(data); this.bluetoothManager.addEventListener( 'connection-status-changed', this._statusChangeHandler ); } } /** * Unsubscribe from bluetooth manager events * @private */ _unsubscribeFromEvents() { if (this.bluetoothManager && this._statusChangeHandler) { this.bluetoothManager.removeEventListener( 'connection-status-changed', this._statusChangeHandler ); } } /** * Handle connection status change event * @private * @param {Object} data - Event data */ _onStatusChanged(data) { // Get full connection info for complete state update const info = this.bluetoothManager.getConnectionInfo(); this._updateStatus(info); } /** * Update component state from connection info * @private * @param {Object} info - Connection information object */ _updateStatus(info) { this.state.status = info.status; this.state.deviceName = info.deviceName; this.state.lastError = info.lastError; this.state.reconnectAttempts = info.reconnectAttempts; this.state.isReconnecting = info.isReconnecting; this.state.timestamp = info.timestamp; // Check if bypassed on this device const posConfigId = this.props.posConfigId || 1; if (this.storageManager.getBypassStatus(posConfigId)) { this.state.status = 'bypassed'; } } /** * Get CSS class for status indicator * @returns {string} CSS class name */ get statusClass() { const baseClass = 'bluetooth-status-indicator'; const statusClass = `${baseClass}-${this.state.status}`; const animationClass = this.state.status === 'connecting' ? 'bluetooth-status-pulse' : ''; return `${baseClass} ${statusClass} ${animationClass}`.trim(); } /** * Get icon for status indicator * @returns {string} Icon class name */ get statusIcon() { switch (this.state.status) { case 'connected': return 'fa fa-bluetooth'; case 'disconnected': return 'fa fa-bluetooth-b'; case 'connecting': return 'fa fa-bluetooth'; case 'bypassed': return 'fa fa-ban'; case 'error': return 'fa fa-exclamation-triangle'; default: return 'fa fa-bluetooth-b'; } } /** * Get human-readable status text * @returns {string} Status text */ get statusText() { switch (this.state.status) { case 'connected': return this.state.deviceName ? `Connected to ${this.state.deviceName}` : 'Connected'; case 'disconnected': return 'Printer Disconnected'; case 'connecting': return this.state.isReconnecting ? `Reconnecting... (${this.state.reconnectAttempts}/3)` : 'Connecting...'; case 'bypassed': return 'Printer Bypassed (Web Print)'; case 'error': return 'Connection Error'; default: return 'Unknown Status'; } } /** * Get detailed tooltip content * @returns {string} Tooltip HTML content */ get tooltipContent() { const lines = []; lines.push(`Status: ${this.statusText}`); if (this.state.deviceName) { lines.push(`Device: ${this.state.deviceName}`); } if (this.state.status === 'connecting' && this.state.isReconnecting) { lines.push(`Reconnect Attempts: ${this.state.reconnectAttempts}/3`); } if (this.state.lastError) { lines.push(`Last Error: ${this.state.lastError}`); } if (this.state.timestamp) { const date = new Date(this.state.timestamp); const timeStr = date.toLocaleTimeString(); lines.push(`Last Update: ${timeStr}`); } return lines.join('
'); } /** * Show tooltip */ onMouseEnter() { this.state.showTooltip = true; } /** * Hide tooltip */ onMouseLeave() { this.state.showTooltip = false; } /** * Handle click on status indicator * Opens the Bluetooth printer configuration dialog */ async onClick() { console.log('Bluetooth status clicked:', this.state); // Open the configuration dialog try { this.dialog.add(BluetoothPrinterConfig, { bluetoothManager: this.bluetoothManager, posConfigId: this.props.posConfigId || 1, }); console.log('Dialog opened successfully'); } catch (error) { console.error('Failed to open dialog:', error); this.notification.add("Failed to open configuration dialog", { type: "danger" }); } } } export default BluetoothConnectionStatus;