fix(home): shrink frame card, three-state status, draggable sheet, label overlap
CI / test (push) Has been cancelled

- HomeView clears the bottom nav so + Add Photo isn't covered.
- Cap large frame-card preview to min(240px, 30dvh) so portrait frames
  no longer dominate the screen at full mobile width.
- Three-state device status — green/Online (recent sync), yellow/Sync
  issue (one window missed), red/Offline (two+ windows missed). Window
  is rotationIntervalMinutes for interval-mode devices, 24h for daily
  wakeHour-mode devices.
- Show last-sync ("synced 2h ago") and next-expected-sync line on the
  large card. wakeHour devices show local-hour ("next sync ~4 AM
  tomorrow") in the device's configured timezone.
- BaseBottomSheet drag-to-dismiss on the handle. Touch and pointer
  events; releases past 80px close the sheet. Snaps back below.
- BaseInput floating label rewrite — taller field, label re-anchors
  to top: 8px when filled/focused so it sits cleanly above the value
  instead of overlapping it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-06 18:23:35 -04:00
parent 5fcfb806be
commit 78ff21fb98
20 changed files with 486 additions and 59 deletions
+21 -10
View File
@@ -19,27 +19,38 @@ describe('FrameCard', () => {
expect(wrapper.text()).toContain('Living Room')
})
it('does not show status badge when status is ok', () => {
it('shows "Online" status when status is ok', () => {
const wrapper = mount(FrameCard, { props: defaultProps })
expect(wrapper.find('.frame-card__status-badge').exists()).toBe(false)
expect(wrapper.find('.frame-card__status-line').text()).toContain('Online')
})
it('shows "Offline" badge when status is offline', () => {
it('shows "Offline" status when status is offline', () => {
const wrapper = mount(FrameCard, {
props: { ...defaultProps, status: 'offline' },
})
const badge = wrapper.find('.frame-card__status-badge')
expect(badge.exists()).toBe(true)
expect(badge.text()).toContain('Offline')
expect(wrapper.find('.frame-card__status-line').text()).toContain('Offline')
})
it('shows "Sync issue" badge when status is sync-fail', () => {
it('shows "Sync issue" status when status is sync-fail', () => {
const wrapper = mount(FrameCard, {
props: { ...defaultProps, status: 'sync-fail' },
})
const badge = wrapper.find('.frame-card__status-badge')
expect(badge.exists()).toBe(true)
expect(badge.text()).toContain('Sync issue')
expect(wrapper.find('.frame-card__status-line').text()).toContain('Sync issue')
})
it('renders lastSync and nextSync lines on the large card', () => {
const wrapper = mount(FrameCard, {
props: { ...defaultProps, lastSync: '2h ago', nextSync: 'next sync in 4h' },
})
const sync = wrapper.find('.frame-card__sync-line')
expect(sync.exists()).toBe(true)
expect(sync.text()).toContain('synced 2h ago')
expect(sync.text()).toContain('next sync in 4h')
})
it('omits the sync line on the large card when no sync info is provided', () => {
const wrapper = mount(FrameCard, { props: defaultProps })
expect(wrapper.find('.frame-card__sync-line').exists()).toBe(false)
})
it('applies offline modifier class when status is offline', () => {