Implement mobile-first navigation with tabs, server-side auto-connect, improve mobile UX (#1008)

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: thomasnordquist <7721625+thomasnordquist@users.noreply.github.com>
Co-authored-by: Thomas Nordquist <thomasnordquist@users.noreply.github.com>
This commit is contained in:
Copilot
2025-12-27 17:02:49 +01:00
committed by GitHub
parent 8f86d272c7
commit 4de52aba7c
45 changed files with 1381 additions and 224 deletions

View File

@@ -91,3 +91,45 @@ When modifying or creating UI components, follow the styling patterns documented
- Access theme colors via `theme.palette.*`, spacing via `theme.spacing()`, typography via `theme.typography.*`
- Support both light and dark modes with theme-conditional styling
- Import Material-UI colors: `import { blueGrey, amber, green, red } from '@mui/material/colors'`
## Mobile Testing Workflow
**Prerequisites for mobile testing:**
```bash
# Install Playwright browsers
npx playwright install --with-deps chromium
# Configure mosquitto to allow anonymous connections (for local testing)
echo "listener 1883
allow_anonymous true" | sudo tee /etc/mosquitto/conf.d/allow-anonymous.conf
sudo systemctl restart mosquitto
```
**Interactive testing with mobile viewport:**
```bash
# Set up environment
export MQTT_EXPLORER_SKIP_AUTH=true
export MQTT_AUTO_CONNECT_HOST=127.0.0.1
# Build and start server
yarn build:server
node dist/src/server.js
# In another terminal, run Playwright test with mobile viewport
# Create test script with viewport: { width: 412, height: 914 }
# Always INSPECT the rendered output, don't rely on assumptions
```
**Key lesson**: Mobile tree visibility issues often stem from:
1. CSS flex/absolute positioning conflicts
2. Missing Redux state updates (connection not propagated to frontend)
3. MQTT broker authentication (mosquitto requires `allow_anonymous true` for testing)
4. Timing issues (frontend subscribing to events after backend emits them)
**Server-side auto-connect** (for testing):
```bash
export MQTT_AUTO_CONNECT_HOST=127.0.0.1
export MQTT_AUTO_CONNECT_PORT=1883 # optional
export MQTT_AUTO_CONNECT_PROTOCOL=mqtt # optional
```

View File

@@ -176,8 +176,12 @@ jobs:
- name: Build Browser Mode
run: yarn build:server
- name: Generate Mobile Demo Video
id: generate_video
continue-on-error: true
run: ./scripts/uiTestsMobile.sh
- name: Post-processing
if: always()
continue-on-error: true
run: ./scripts/prepareVideoMobile.sh
- name: Generate unique base path
id: basepath
@@ -199,24 +203,32 @@ jobs:
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: 'eu-central-1'
- name: Upload full mobile video to S3
if: always()
continue-on-error: true
env:
AWS_BUCKET: ${{ vars.AWS_BUCKET }}
BASEPATH: ${{ steps.basepath.outputs.basepath }}
run: |
# Upload GIF
aws s3api put-object \
--bucket ${AWS_BUCKET} \
--key artifacts/${BASEPATH}/ui-test-mobile.gif \
--body ./ui-test-mobile.gif \
--content-type image/gif
# Upload GIF if it exists
if [ -f ./ui-test-mobile.gif ]; then
aws s3api put-object \
--bucket ${AWS_BUCKET} \
--key artifacts/${BASEPATH}/ui-test-mobile.gif \
--body ./ui-test-mobile.gif \
--content-type image/gif
fi
# Upload MP4
aws s3api put-object \
--bucket ${AWS_BUCKET} \
--key artifacts/${BASEPATH}/ui-test-mobile.mp4 \
--body ./ui-test-mobile.mp4 \
--content-type video/mp4
# Upload MP4 if it exists
if [ -f ./ui-test-mobile.mp4 ]; then
aws s3api put-object \
--bucket ${AWS_BUCKET} \
--key artifacts/${BASEPATH}/ui-test-mobile.mp4 \
--body ./ui-test-mobile.mp4 \
--content-type video/mp4
fi
- name: Upload mobile video segments to S3
if: always()
continue-on-error: true
env:
AWS_BUCKET: ${{ vars.AWS_BUCKET }}
BASEPATH: ${{ steps.basepath.outputs.basepath }}
@@ -234,6 +246,7 @@ jobs:
done
shopt -u nullglob # Restore default behavior
- name: Generate file URLs
if: always()
id: fileurl
env:
AWS_BUCKET: ${{ vars.AWS_BUCKET }}
@@ -243,20 +256,24 @@ jobs:
echo "base-url=${BASE_URL}" >> $GITHUB_OUTPUT
echo "Uploaded to: ${BASE_URL}"
- name: Generate markdown summary
if: always()
id: markdown
env:
BASE_URL: ${{ steps.fileurl.outputs.base-url }}
TEST_STATUS: ${{ steps.generate_video.outcome }}
run: |
MARKDOWN=$(node ./scripts/generateMarkdownSummaryMobile.js "${BASE_URL}")
MARKDOWN=$(node ./scripts/generateMarkdownSummaryMobile.js "${BASE_URL}" "${TEST_STATUS}")
echo "markdown<<EOF" >> $GITHUB_OUTPUT
echo "$MARKDOWN" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Add to workflow summary
if: always()
env:
MARKDOWN: ${{ steps.markdown.outputs.markdown }}
run: |
echo "$MARKDOWN" >> $GITHUB_STEP_SUMMARY
- name: Post mobile video to PR
if: always()
uses: actions/github-script@v7
env:
MARKDOWN: ${{ steps.markdown.outputs.markdown }}