feat: floor plan viewport rendering, per-canvas config, server media upload
- Render floor plan inside React Flow ViewportPortal so it pans/zooms with nodes (was screen-fixed, desynced on pan/zoom); zoom-stable resize handles. - Move floor plan config from the left panel into the canvas (design) edit modal; attach per-design and fix cross-design bleed on load. - Store images via a new generic backend media endpoint (POST/GET/DELETE /api/v1/media) on disk under <data_dir>/uploads, not base64 in the canvas. - Disable floor plans in standalone mode (no backend to upload/serve); drop base64 localStorage persistence. See ADR-001 in CLAUDE.md. - Tests: backend media route, DesignModal floor plan + upload, store floorMap. ha-relevant: maybe
This commit is contained in:
@@ -1305,4 +1305,37 @@ describe('canvasStore — custom style apply', () => {
|
||||
expect(n.width).toBe(300)
|
||||
expect(n.height).toBe(100)
|
||||
})
|
||||
|
||||
describe('floorMap', () => {
|
||||
const fm = {
|
||||
imageData: 'data:image/png;base64,abc',
|
||||
posX: 10, posY: 20, width: 800, height: 600,
|
||||
opacity: 0.8, locked: false, enabled: true,
|
||||
}
|
||||
|
||||
beforeEach(() => useCanvasStore.setState({ floorMap: null, hasUnsavedChanges: false }))
|
||||
|
||||
it('setFloorMap sets and marks unsaved', () => {
|
||||
useCanvasStore.getState().setFloorMap(fm)
|
||||
expect(useCanvasStore.getState().floorMap).toEqual(fm)
|
||||
expect(useCanvasStore.getState().hasUnsavedChanges).toBe(true)
|
||||
})
|
||||
|
||||
it('setFloorMap(null) clears the plan — prevents cross-design bleed on load', () => {
|
||||
useCanvasStore.setState({ floorMap: fm })
|
||||
useCanvasStore.getState().setFloorMap(null)
|
||||
expect(useCanvasStore.getState().floorMap).toBeNull()
|
||||
})
|
||||
|
||||
it('updateFloorMap patches an existing plan (position preserved)', () => {
|
||||
useCanvasStore.setState({ floorMap: fm })
|
||||
useCanvasStore.getState().updateFloorMap({ posX: 99, opacity: 0.5 })
|
||||
expect(useCanvasStore.getState().floorMap).toEqual({ ...fm, posX: 99, opacity: 0.5 })
|
||||
})
|
||||
|
||||
it('updateFloorMap is a no-op when no plan exists', () => {
|
||||
useCanvasStore.getState().updateFloorMap({ posX: 5 })
|
||||
expect(useCanvasStore.getState().floorMap).toBeNull()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user