import { api } from '../api.js'; import { showToast } from '../components/toast.js'; import { esc } from '../utils.js'; const API = (url, opts = {}) => fetch('/api' + url, { headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${localStorage.getItem('token')}`, ...opts.headers }, ...opts }).then(r => r.json()); export async function render(container) { const hash = window.location.hash; if (hash.startsWith('#/wall/')) { const id = hash.split('#/wall/')[1]; return renderWallEditor(container, id); } return renderList(container); } async function renderList(container) { container.innerHTML = `
`; document.getElementById('newWallBtn').onclick = async () => { const name = prompt('Video wall name:'); if (!name) return; const wall = await API('/walls', { method: 'POST', body: JSON.stringify({ name }) }); window.location.hash = `#/wall/${wall.id}`; }; try { const walls = await API('/walls'); const grid = document.getElementById('wallGrid'); if (!walls.length) { grid.innerHTML = '

No video walls yet

Create a video wall to combine multiple displays.

'; return; } grid.innerHTML = walls.map(w => `
${Array.from({ length: w.grid_cols * w.grid_rows }, (_, i) => { const row = Math.floor(i / w.grid_cols); const col = i % w.grid_cols; const dev = w.devices?.find(d => d.grid_col === col && d.grid_row === row); return `
${dev?.device_name?.slice(0, 6) || ''}
`; }).join('')}
${w.name}
${w.grid_cols}x${w.grid_rows} grid • ${w.devices?.length || 0} devices
`).join(''); } catch (err) { showToast(err.message, 'error'); } } async function renderWallEditor(container, wallId) { let wall, devices; try { [wall, devices] = await Promise.all([API(`/walls/${wallId}`), api.getDevices()]); } catch { container.innerHTML = '

Wall not found

'; return; } const content = await api.getContent(); const unassigned = devices.filter(d => !wall.devices?.find(wd => wd.device_id === d.id)); container.innerHTML = ` Back to Video Walls

Grid Configuration

Content

Available Displays

${unassigned.map(d => `
${d.name}
${d.status}
`).join('') || '

All devices assigned

'}
`; function renderGrid() { const cols = parseInt(document.getElementById('gridCols').value) || 2; const rows = parseInt(document.getElementById('gridRows').value) || 2; const grid = document.getElementById('wallGrid'); grid.style.gridTemplateColumns = `repeat(${cols}, 120px)`; let html = ''; for (let r = 0; r < rows; r++) { for (let c = 0; c < cols; c++) { const dev = wall.devices?.find(d => d.grid_col === c && d.grid_row === r); html += `
${dev ? `
${dev.device_name}
[${c},${r}]
` : `
Drop here
[${c},${r}]
`}
`; } } grid.innerHTML = html; // Drop targets grid.querySelectorAll('[data-grid-col]').forEach(cell => { cell.ondragover = (e) => { e.preventDefault(); cell.style.borderColor = 'var(--success)'; }; cell.ondragleave = () => { cell.style.borderColor = ''; }; cell.ondrop = async (e) => { e.preventDefault(); cell.style.borderColor = ''; const deviceId = e.dataTransfer.getData('device-id'); const deviceName = e.dataTransfer.getData('device-name'); const col = parseInt(cell.dataset.gridCol); const row = parseInt(cell.dataset.gridRow); // Add to wall devices const existing = wall.devices?.filter(d => !(d.grid_col === col && d.grid_row === row)) || []; existing.push({ device_id: deviceId, device_name: deviceName, grid_col: col, grid_row: row }); try { const updated = await API(`/walls/${wallId}/devices`, { method: 'PUT', body: JSON.stringify({ devices: existing }) }); wall.devices = updated.devices; renderGrid(); showToast(`${deviceName} placed at [${col},${row}]`, 'success'); } catch (err) { showToast(err.message, 'error'); } }; }); } // Drag sources container.querySelectorAll('[draggable]').forEach(el => { el.ondragstart = (e) => { e.dataTransfer.setData('device-id', el.dataset.deviceId); e.dataTransfer.setData('device-name', el.dataset.deviceName); }; }); document.getElementById('updateGridBtn').onclick = async () => { try { await API(`/walls/${wallId}`, { method: 'PUT', body: JSON.stringify({ grid_cols: parseInt(document.getElementById('gridCols').value), grid_rows: parseInt(document.getElementById('gridRows').value), bezel_h_mm: parseFloat(document.getElementById('bezelH').value), bezel_v_mm: parseFloat(document.getElementById('bezelV').value), })}); wall.grid_cols = parseInt(document.getElementById('gridCols').value); wall.grid_rows = parseInt(document.getElementById('gridRows').value); renderGrid(); showToast('Grid updated', 'success'); } catch (err) { showToast(err.message, 'error'); } }; document.getElementById('setContentBtn').onclick = async () => { const contentId = document.getElementById('wallContent').value; try { await API(`/walls/${wallId}/content`, { method: 'PUT', body: JSON.stringify({ content_id: contentId || null }) }); showToast('Content updated', 'success'); } catch (err) { showToast(err.message, 'error'); } }; document.getElementById('deleteWallBtn').onclick = async () => { try { await API(`/walls/${wallId}`, { method: 'DELETE' }); showToast('Wall deleted', 'success'); window.location.hash = '#/walls'; } catch (err) { showToast(err.message, 'error'); } }; renderGrid(); } export function cleanup() {}