/** @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;