// Inject the HTML before Alpine initializes its data components
if (!document.querySelector('.container')) {
document.body.innerHTML = `
| Device Name |
Manufacturer |
Year |
CPU |
RAM |
Display |
Battery |
Actions |
|
|
|
|
|
|
|
|
|
No devices found. Try adding one!
Manufacturer:
Release Year:
CPU:
RAM:
Storage:
Display:
Battery Life:
Average Rating:
`;
}
document.addEventListener('alpine:init', () => {
Alpine.data('deviceManager', () => ({
devices: [],
searchQuery: '',
showCreateForm: false,
showDetailsModal: false,
showToast: false,
toastMessage: '',
editingDevice: null,
selectedDevice: null,
// Use config from server or default
apiBase: window.APP_CONFIG?.api_url || '',
init() {
this.$watch('showCreateForm', value => {
const popover = document.getElementById('createFormPopover');
if (value) {
try { popover.showPopover(); } catch(e) {}
} else {
try { popover.hidePopover(); } catch(e) {}
}
});
this.$watch('showDetailsModal', value => {
const popover = document.getElementById('detailsModalPopover');
if (value) {
try { popover.showPopover(); } catch(e) {}
} else {
try { popover.hidePopover(); } catch(e) {}
}
});
this.$watch('showToast', value => {
const popover = document.getElementById('toastPopover');
if (value) {
try { popover.showPopover(); } catch(e) {}
} else {
try { popover.hidePopover(); } catch(e) {}
}
});
},
form: {
name: '',
manufacturer: '',
release_year: null,
cpu: '',
ram_mb: null,
storage_mb: null,
display_size: '',
battery_hours: null
},
get filteredDevices() {
return this.devices.filter(device =>
device.name.toLowerCase().includes(this.searchQuery.toLowerCase()) ||
device.manufacturer.toLowerCase().includes(this.searchQuery.toLowerCase())
);
},
async loadDevices() {
try {
const response = await fetch(this.apiBase + '/devices');
const data = await response.json();
console.log('Devices data:', data);
this.devices = Array.isArray(data.data) ? data.data : (Array.isArray(data) ? data : []);
} catch (error) {
this.showNotification('Failed to load devices');
console.error(error);
}
},
connectWebSocket() {
const wsUrl = this.apiBase.replace('http', 'ws');
console.log('Connecting to WebSocket:', wsUrl);
const socket = new WebSocket(wsUrl);
socket.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('WS Message received:', data);
if (data.event_type === 'DevicePublished') {
this.showNotification(`New device: ${data.device_name}`);
this.loadDevices(); // Refresh list
} else if (data.event_type === 'DeviceUpdated') {
this.showNotification(`Device updated: ${data.device_name}`);
this.loadDevices(); // Refresh list
} else if (data.event_type === 'DeviceDeleted') {
this.showNotification(`Device removed: ${data.device_name}`);
// Optimistically remove from local list or just refresh
this.devices = this.devices.filter(d => d.id != data.device_id);
if (this.selectedDevice && this.selectedDevice.id == data.device_id) {
this.showDetailsModal = false;
}
}
};
socket.onclose = () => {
console.log('WS disconnected, retrying in 5s...');
setTimeout(() => this.connectWebSocket(), 5000);
};
socket.onerror = (error) => {
console.error('WS Error:', error);
};
},
async saveDevice() {
try {
const url = this.editingDevice
? `${this.apiBase}/devices/${this.editingDevice.id}`
: `${this.apiBase}/devices`;
const method = this.editingDevice ? 'PUT' : 'POST';
// Filter out null/undefined values and only send necessary data
const payload = {
name: this.form.name,
manufacturer: this.form.manufacturer,
release_year: this.form.release_year ? parseInt(this.form.release_year) : null,
cpu: this.form.cpu || null,
ram_mb: this.form.ram_mb ? parseInt(this.form.ram_mb) : null,
storage_mb: this.form.storage_mb ? parseInt(this.form.storage_mb) : null,
display_size: this.form.display_size || null,
battery_hours: this.form.battery_hours ? parseFloat(this.form.battery_hours) : null
};
const response = await fetch(url, {
method,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
if (response.ok) {
await this.loadDevices();
this.resetForm();
this.showCreateForm = false;
this.showNotification(
this.editingDevice ? 'Device updated successfully!' : 'Device created successfully!'
);
} else {
const errorData = await response.json().catch(() => ({}));
this.showNotification(`Failed to save device: ${errorData.error || response.statusText}`);
}
} catch (error) {
this.showNotification('Failed to save device: Network error');
console.error(error);
}
},
editDevice(device) {
this.editingDevice = device;
this.form = { ...device };
this.showCreateForm = true;
this.showDetailsModal = false;
},
async deleteDevice(id) {
if (confirm('Are you sure you want to delete this device?')) {
try {
const response = await fetch(`${this.apiBase}/devices/${id}`, {
method: 'DELETE'
});
if (response.ok) {
await this.loadDevices();
this.showNotification('Device deleted successfully!');
} else {
this.showNotification('Failed to delete device');
}
} catch (error) {
this.showNotification('Failed to delete device');
console.error(error);
}
}
},
viewDevice(device) {
this.selectedDevice = device;
this.showDetailsModal = true;
},
resetForm() {
this.form = {
name: '',
manufacturer: '',
release_year: null,
cpu: '',
ram_mb: null,
storage_mb: null,
display_size: '',
battery_hours: null
};
this.editingDevice = null;
},
showNotification(message) {
this.toastMessage = message;
this.showToast = true;
setTimeout(() => {
this.showToast = false;
}, 3000);
},
addNewDevice() {
this.resetForm();
this.showCreateForm = true;
}
}));
});