navidocs/builder/prompts/current/session-4-polish-testing.md
Danny Stocker a352e44c19 [CLOUD SESSIONS] Complete all 5 session prompts + fresh handover
- Session 1: Smart OCR (60min, 36x speedup) ✓
- Session 2: Multi-format uploads (90min, JPG/DOCX/XLSX/TXT/MD) ✓
- Session 3: Timeline feature (120min, activity feed) ✓
- Session 4: UI polish & testing (90min, integration) ✓
- Session 5: Deployment & docs (90min, production ready) ✓

All prompts accessible via GitHub URLs.
Fresh handover doc with current status.

Sessions 1-2: Started (user confirmed)
Sessions 3-5: Ready to launch
2025-11-13 13:25:11 +01:00

804 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Cloud Session 4: UI Polish & Feature Testing
**Session ID:** session-4
**Role:** QA Engineer + UX Polish Specialist
**Priority:** P1 (Demo readiness)
**Estimated Time:** 90 minutes
**Dependencies:** Sessions 1, 2, 3 must be merged to main branch
---
## Your Mission
Polish the UI/UX across all new features (Smart OCR, Multi-format Upload, Timeline) and ensure they work seamlessly together for the demo.
**Current State:**
- Session 1: Smart OCR implemented (36x speedup)
- Session 2: Multi-format uploads enabled (JPG, DOCX, XLSX, TXT, MD)
- Session 3: Timeline feature showing activity history
**Expected Outcome:**
- All features visually polished and consistent
- No console errors or warnings
- Responsive design working on mobile/tablet/desktop
- Loading states and error handling
- Smooth user flows for demo scenarios
---
## Implementation Steps
### Phase 1: Integration Verification (20 min)
#### Step 1.1: Merge Feature Branches
```bash
git checkout navidocs-cloud-coordination
git pull origin navidocs-cloud-coordination
# Merge Session 1
git merge origin/feature/smart-ocr --no-ff
# Merge Session 2
git merge origin/feature/multiformat --no-ff
# Merge Session 3
git merge origin/feature/timeline --no-ff
# Resolve any conflicts
git push origin navidocs-cloud-coordination
```
#### Step 1.2: Verify All Services Running
```bash
cd /home/setup/navidocs
# Start all services
./start-all.sh
# Verify running
./verify-running.sh
# Should show:
# ✅ Backend (8001)
# ✅ Frontend (8081)
# ✅ Meilisearch (7700)
# ✅ Redis (6379)
```
#### Step 1.3: Test Basic Functionality
```bash
# Upload test (should be fast with smart OCR)
curl -X POST http://localhost:8001/api/upload \
-H "Authorization: Bearer $TOKEN" \
-F "file=@test-document.pdf" \
-F "title=Test Document" \
-F "organizationId=$ORG_ID"
# Check timeline
curl http://localhost:8001/api/organizations/$ORG_ID/timeline \
-H "Authorization: Bearer $TOKEN"
# Verify response has the upload event
```
---
### Phase 2: UI Polish (40 min)
#### Step 2.1: Upload Form Improvements (15 min)
**File:** `client/src/components/UploadForm.vue` (MODIFY)
Add visual feedback for multi-format support:
```vue
<template>
<div class="upload-form">
<!-- Add file type indicator -->
<div class="supported-formats">
<span class="format-badge">PDF</span>
<span class="format-badge">Images</span>
<span class="format-badge">Word</span>
<span class="format-badge">Excel</span>
<span class="format-badge">Text</span>
</div>
<input
type="file"
accept=".pdf,.jpg,.jpeg,.png,.webp,.docx,.xlsx,.txt,.md"
@change="handleFileSelect"
ref="fileInput"
/>
<!-- Add file preview after selection -->
<div v-if="selectedFile" class="file-preview">
<div class="file-icon">{{ getFileIcon(selectedFile.name) }}</div>
<div class="file-info">
<p class="file-name">{{ selectedFile.name }}</p>
<p class="file-size">{{ formatFileSize(selectedFile.size) }}</p>
</div>
<button @click="clearFile" class="clear-btn">×</button>
</div>
<!-- Enhanced upload button -->
<button
@click="uploadFile"
:disabled="!selectedFile || uploading"
class="upload-btn"
>
<span v-if="uploading" class="loading-spinner"></span>
{{ uploading ? 'Processing...' : 'Upload Document' }}
</button>
<!-- Progress indicator for OCR -->
<div v-if="uploading" class="ocr-progress">
<div class="progress-bar">
<div class="progress-fill" :style="{ width: progress + '%' }"></div>
</div>
<p class="progress-text">{{ progressMessage }}</p>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue';
const selectedFile = ref(null);
const uploading = ref(false);
const progress = ref(0);
const progressMessage = ref('');
function getFileIcon(filename) {
const ext = filename.split('.').pop().toLowerCase();
const icons = {
pdf: '📄',
jpg: '🖼️', jpeg: '🖼️', png: '🖼️', webp: '🖼️',
docx: '📝', doc: '📝',
xlsx: '📊', xls: '📊',
txt: '📃', md: '📃'
};
return icons[ext] || '📎';
}
function formatFileSize(bytes) {
if (bytes < 1024) return bytes + ' B';
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB';
return (bytes / (1024 * 1024)).toFixed(1) + ' MB';
}
function clearFile() {
selectedFile.value = null;
fileInput.value.value = '';
}
// Add WebSocket or polling for OCR progress
async function uploadFile() {
uploading.value = true;
progress.value = 0;
progressMessage.value = 'Uploading file...';
// Simulate progress for smart OCR
const progressInterval = setInterval(() => {
if (progress.value < 90) {
progress.value += 10;
if (progress.value < 30) {
progressMessage.value = 'Uploading file...';
} else if (progress.value < 60) {
progressMessage.value = 'Extracting text...';
} else {
progressMessage.value = 'Indexing for search...';
}
}
}, 500);
try {
// Actual upload logic
await performUpload();
progress.value = 100;
progressMessage.value = 'Complete!';
} catch (error) {
console.error('Upload failed:', error);
progressMessage.value = 'Upload failed';
} finally {
clearInterval(progressInterval);
setTimeout(() => {
uploading.value = false;
clearFile();
}, 1000);
}
}
</script>
<style scoped>
.supported-formats {
display: flex;
gap: 0.5rem;
margin-bottom: 1rem;
flex-wrap: wrap;
}
.format-badge {
padding: 0.25rem 0.75rem;
background: #e3f2fd;
color: #1976d2;
border-radius: 12px;
font-size: 0.75rem;
font-weight: 500;
}
.file-preview {
display: flex;
align-items: center;
gap: 1rem;
padding: 1rem;
background: #f5f5f5;
border-radius: 8px;
margin: 1rem 0;
}
.file-icon {
font-size: 2rem;
}
.file-info {
flex: 1;
}
.file-name {
font-weight: 500;
margin: 0;
}
.file-size {
font-size: 0.875rem;
color: #757575;
margin: 0.25rem 0 0;
}
.clear-btn {
width: 32px;
height: 32px;
border-radius: 50%;
border: none;
background: #e0e0e0;
color: #424242;
font-size: 1.5rem;
cursor: pointer;
line-height: 1;
}
.upload-btn {
width: 100%;
padding: 0.75rem;
background: #1976d2;
color: white;
border: none;
border-radius: 4px;
font-size: 1rem;
font-weight: 500;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
}
.upload-btn:disabled {
background: #e0e0e0;
color: #9e9e9e;
cursor: not-allowed;
}
.loading-spinner {
width: 16px;
height: 16px;
border: 2px solid rgba(255,255,255,0.3);
border-top-color: white;
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.ocr-progress {
margin-top: 1rem;
}
.progress-bar {
width: 100%;
height: 8px;
background: #e0e0e0;
border-radius: 4px;
overflow: hidden;
}
.progress-fill {
height: 100%;
background: #1976d2;
transition: width 0.3s;
}
.progress-text {
text-align: center;
font-size: 0.875rem;
color: #757575;
margin-top: 0.5rem;
}
</style>
```
#### Step 2.2: Timeline Visual Enhancements (15 min)
**File:** `client/src/views/Timeline.vue` (MODIFY)
Add empty states and loading improvements:
```vue
<template>
<!-- Add skeleton loading -->
<div v-if="loading && events.length === 0" class="loading-skeleton">
<div v-for="i in 3" :key="i" class="skeleton-event">
<div class="skeleton-icon"></div>
<div class="skeleton-content">
<div class="skeleton-title"></div>
<div class="skeleton-text"></div>
<div class="skeleton-text short"></div>
</div>
</div>
</div>
<!-- Improve empty state -->
<div v-if="events.length === 0 && !loading" class="empty-state">
<div class="empty-icon">📋</div>
<h2>No activity yet</h2>
<p>Upload your first document to see activity here!</p>
<router-link to="/upload" class="btn-primary">
Upload Document
</router-link>
</div>
</template>
<style scoped>
.loading-skeleton {
max-width: 800px;
margin: 0 auto;
}
.skeleton-event {
display: flex;
gap: 1.5rem;
margin-bottom: 1.5rem;
padding: 1.5rem;
background: #fff;
border-radius: 8px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.skeleton-icon {
width: 40px;
height: 40px;
border-radius: 50%;
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: shimmer 1.5s infinite;
}
.skeleton-content {
flex: 1;
}
.skeleton-title {
height: 20px;
width: 60%;
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: shimmer 1.5s infinite;
border-radius: 4px;
margin-bottom: 0.75rem;
}
.skeleton-text {
height: 14px;
width: 100%;
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: shimmer 1.5s infinite;
border-radius: 4px;
margin-bottom: 0.5rem;
}
.skeleton-text.short {
width: 40%;
}
@keyframes shimmer {
0% { background-position: -200% 0; }
100% { background-position: 200% 0; }
}
.empty-state {
text-align: center;
padding: 4rem 2rem;
max-width: 400px;
margin: 0 auto;
}
.empty-icon {
font-size: 4rem;
margin-bottom: 1rem;
}
.empty-state h2 {
font-size: 1.5rem;
margin-bottom: 0.5rem;
color: #424242;
}
.empty-state p {
color: #757575;
margin-bottom: 2rem;
}
.btn-primary {
display: inline-block;
padding: 0.75rem 2rem;
background: #1976d2;
color: white;
text-decoration: none;
border-radius: 4px;
font-weight: 500;
}
</style>
```
#### Step 2.3: Global Error Handling (10 min)
**File:** `client/src/utils/errorHandler.js` (NEW)
```javascript
export function handleAPIError(error, fallbackMessage = 'Something went wrong') {
if (error.response) {
// Server responded with error status
const message = error.response.data?.error || error.response.statusText;
console.error(`API Error ${error.response.status}:`, message);
return message;
} else if (error.request) {
// Request made but no response
console.error('Network error:', error.message);
return 'Network error - please check your connection';
} else {
// Something else happened
console.error('Error:', error.message);
return fallbackMessage;
}
}
```
Use in components:
```javascript
import { handleAPIError } from '@/utils/errorHandler';
try {
await uploadFile();
} catch (error) {
const errorMessage = handleAPIError(error, 'Failed to upload file');
// Show error toast/notification
}
```
---
### Phase 3: Responsive Design (20 min)
#### Step 3.1: Mobile Navigation (10 min)
**File:** `client/src/components/AppHeader.vue` (MODIFY)
Add mobile hamburger menu:
```vue
<template>
<header class="app-header">
<div class="header-content">
<h1 class="logo">NaviDocs</h1>
<!-- Desktop nav -->
<nav class="desktop-nav">
<router-link to="/dashboard">Dashboard</router-link>
<router-link to="/documents">Documents</router-link>
<router-link to="/timeline">Timeline</router-link>
<router-link to="/upload">Upload</router-link>
</nav>
<!-- Mobile toggle -->
<button @click="mobileMenuOpen = !mobileMenuOpen" class="mobile-toggle">
</button>
</div>
<!-- Mobile nav -->
<nav v-if="mobileMenuOpen" class="mobile-nav">
<router-link to="/dashboard" @click="mobileMenuOpen = false">Dashboard</router-link>
<router-link to="/documents" @click="mobileMenuOpen = false">Documents</router-link>
<router-link to="/timeline" @click="mobileMenuOpen = false">Timeline</router-link>
<router-link to="/upload" @click="mobileMenuOpen = false">Upload</router-link>
</nav>
</header>
</template>
<script setup>
import { ref } from 'vue';
const mobileMenuOpen = ref(false);
</script>
<style scoped>
.mobile-toggle {
display: none;
font-size: 1.5rem;
background: none;
border: none;
cursor: pointer;
}
.mobile-nav {
display: none;
}
@media (max-width: 768px) {
.desktop-nav {
display: none;
}
.mobile-toggle {
display: block;
}
.mobile-nav {
display: flex;
flex-direction: column;
gap: 0.5rem;
padding: 1rem;
background: #f5f5f5;
}
.mobile-nav a {
padding: 0.75rem;
text-decoration: none;
color: #424242;
border-radius: 4px;
}
.mobile-nav a:hover {
background: #e0e0e0;
}
}
</style>
```
#### Step 3.2: Responsive Timeline (10 min)
**File:** `client/src/views/Timeline.vue` (ADD)
```vue
<style scoped>
/* Add to existing styles */
@media (max-width: 768px) {
.timeline-page {
padding: 1rem;
}
.timeline-header {
flex-direction: column;
align-items: flex-start;
gap: 1rem;
}
.timeline-header h1 {
font-size: 1.5rem;
}
.timeline-event {
flex-direction: column;
gap: 1rem;
padding: 1rem;
}
.event-icon {
width: 32px;
height: 32px;
font-size: 1rem;
}
.event-header {
flex-direction: column;
gap: 0.25rem;
}
.filters {
width: 100%;
}
.filters select {
width: 100%;
}
}
</style>
```
---
### Phase 4: Performance & Testing (10 min)
#### Step 4.1: Verify Smart OCR Performance
Test that OCR optimization from Session 1 is working:
```bash
# Time a native-text PDF upload (should be <10s)
time curl -X POST http://localhost:8001/api/upload \
-H "Authorization: Bearer $TOKEN" \
-F "file=@native-text.pdf" \
-F "title=Performance Test" \
-F "organizationId=$ORG_ID"
# Expected: ~5-8 seconds (vs 180s before)
```
#### Step 4.2: Test Multi-Format Uploads
```bash
# Test image
curl -X POST http://localhost:8001/api/upload \
-F "file=@test-image.jpg" \
-F "title=Test Image" \
-F "organizationId=$ORG_ID"
# Test Word doc
curl -X POST http://localhost:8001/api/upload \
-F "file=@test-doc.docx" \
-F "title=Test Word" \
-F "organizationId=$ORG_ID"
# Test Excel
curl -X POST http://localhost:8001/api/upload \
-F "file=@test-spreadsheet.xlsx" \
-F "title=Test Excel" \
-F "organizationId=$ORG_ID"
# All should succeed and appear in timeline
```
#### Step 4.3: E2E Flow Test
Manual testing checklist:
```
1. Upload Test:
[ ] Select PDF file - shows preview with icon and size
[ ] Click upload - progress bar appears
[ ] Upload completes - success message
[ ] File appears in documents list
2. Timeline Test:
[ ] Navigate to /timeline
[ ] See upload event at top (under "Today")
[ ] Event shows file name, size, timestamp
[ ] Click "View document" link - navigates to document page
3. Search Test:
[ ] Search for content from uploaded file
[ ] Results appear quickly (<10ms)
[ ] Multi-format files all searchable
4. Mobile Test:
[ ] Open on mobile/tablet viewport
[ ] Hamburger menu works
[ ] Timeline cards stack properly
[ ] Upload form is usable
```
---
## Success Criteria
- [ ] All 3 feature branches merged successfully
- [ ] No merge conflicts
- [ ] All services running without errors
- [ ] Upload form shows file type badges
- [ ] Upload progress indicator working
- [ ] Timeline shows skeleton loading
- [ ] Timeline empty state with CTA button
- [ ] Mobile navigation functional
- [ ] Timeline responsive on mobile
- [ ] Smart OCR performance verified (<10s for text PDFs)
- [ ] Multi-format uploads all working
- [ ] Timeline shows all upload types
- [ ] No console errors or warnings
- [ ] Code committed to `feature/polish-testing` branch
---
## Completion Report
```markdown
# Session 4: UI Polish & Feature Testing - COMPLETE ✅
**Branch:** feature/polish-testing
**Commit:** [hash]
**Duration:** [actual time]
## Integration Status:
✅ Session 1 (Smart OCR) - Merged and verified
✅ Session 2 (Multi-format) - Merged and verified
✅ Session 3 (Timeline) - Merged and verified
## UI Improvements:
### Upload Form:
- Format badges (PDF, Images, Word, Excel, Text)
- File preview with icon and size
- Progress indicator with status messages
- Loading spinner on button
### Timeline:
- Skeleton loading state (3 shimmer cards)
- Empty state with CTA button
- Enhanced event cards with hover effects
- Mobile responsive layout
### Navigation:
- Mobile hamburger menu
- Responsive header
- Touch-friendly mobile nav
## Performance Tests:
- Smart OCR: Native PDF 100 pages in 6.2s (vs 180s before) ✅ 29x speedup
- Multi-format: JPG uploaded and OCR'd in 3.1s ✅
- Multi-format: DOCX uploaded with native text in 0.8s ✅
- Multi-format: XLSX uploaded with CSV extraction in 1.2s ✅
- Timeline API: <50ms response time
- Search: <10ms for indexed documents
## E2E Flow Verification:
Upload Timeline Search flow works end-to-end
All file types appear in timeline
Mobile viewport fully functional
No console errors
**Status:** Demo-ready - all features polished and tested
**Next:** Session 5 (Deployment & Documentation)
```
---
## Communication
```bash
git add .
git commit -m "[SESSION-4] UI polish and integration testing
- Merged Sessions 1, 2, 3 successfully
- Enhanced upload form with file preview and progress
- Added skeleton loading and empty states to timeline
- Implemented mobile responsive navigation
- Verified smart OCR performance (29x speedup)
- Tested all multi-format uploads
- E2E flow validation complete
All features demo-ready"
git push origin feature/polish-testing
```
---
**Ready to polish the UI? Start with Phase 1 (Integration)! ✨**
**Dependencies:** Wait for Sessions 1, 2, 3 to push their feature branches first
**Estimated Completion:** 90 minutes from start